Title: [209486] trunk
Revision
209486
Author
rn...@webkit.org
Date
2016-12-07 14:51:54 -0800 (Wed, 07 Dec 2016)

Log Message

document.caretRangeFromPoint doesn't retarget the resultant Range correctly.
https://bugs.webkit.org/show_bug.cgi?id=165146

Reviewed by Sam Weinig.

Source/WebCore:

The bug was caused by caretRangeFromPoint not retargeting the resultant Range correctly.
Namely, it's possible for RenderObject::positionForPoint to move across shadow boundary
even if node was identically equal to ancestorInThisScope(node).

Fixed the bug by directly retargeting the range's container node and its offset as done
for elementFromPoint in r206795.

Test: fast/shadow-dom/caret-range-from-point-in-shadow-tree.html

* dom/Document.cpp:
(WebCore::Document::caretRangeFromPoint):

LayoutTests:

Added a regression test for caretRangeFromPoint retargeting the result.

* fast/shadow-dom/caret-range-from-point-in-shadow-tree-expected.txt: Added.
* fast/shadow-dom/caret-range-from-point-in-shadow-tree.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (209485 => 209486)


--- trunk/LayoutTests/ChangeLog	2016-12-07 22:51:21 UTC (rev 209485)
+++ trunk/LayoutTests/ChangeLog	2016-12-07 22:51:54 UTC (rev 209486)
@@ -1,3 +1,15 @@
+2016-12-07  Ryosuke Niwa  <rn...@webkit.org>
+
+        document.caretRangeFromPoint doesn't retarget the resultant Range correctly.
+        https://bugs.webkit.org/show_bug.cgi?id=165146
+
+        Reviewed by Sam Weinig.
+
+        Added a regression test for caretRangeFromPoint retargeting the result.
+
+        * fast/shadow-dom/caret-range-from-point-in-shadow-tree-expected.txt: Added.
+        * fast/shadow-dom/caret-range-from-point-in-shadow-tree.html: Added.
+
 2016-12-07  Antoine Quint  <grao...@apple.com>
 
         [Modern Media Controls] Entering fullscreen and returning to inline shows fullscreen controls

Added: trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree-expected.txt (0 => 209486)


--- trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree-expected.txt	2016-12-07 22:51:54 UTC (rev 209486)
@@ -0,0 +1,102 @@
+bye
+host: <i><br></i>, shadowRoot: <b><br></b>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: <i><br></i>, shadowRoot: <b><br></b>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: <i><br></i>, shadowRoot: hi
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: <i><br></i>, shadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)
+PASS range.startContainer is not null
+PASS range.startContainer is host.querySelector("i")
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is not null
+PASS range.startContainer is host.querySelector("i")
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: hi, shadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host.firstChild
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: hi, shadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host.firstChild
+PASS range.startOffset is 2
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host"></span>, innerShadowRoot: hi
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host"></span>, innerShadowRoot: hi
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;">hi</span>, innerShadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 15, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;">hi</span>, innerShadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;"><slot></slot></span>, innerShadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 15, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host.firstChild
+PASS range.startOffset is 0
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+
+host: bye, shadowRoot: <span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;"><slot></slot></span>, innerShadowRoot: <slot></slot>
+range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)
+PASS range.startContainer is host.firstChild
+PASS range.startOffset is 3
+PASS range.startContainer is range.endContainer
+PASS range.startOffset is range.endOffset
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree.html (0 => 209486)


--- trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree.html	                        (rev 0)
+++ trunk/LayoutTests/fast/shadow-dom/caret-range-from-point-in-shadow-tree.html	2016-12-07 22:51:54 UTC (rev 209486)
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div id="test"><span><i><br></i></span></div>
+<div id="console"></div>
+<script src=""
+<style>
+#test { padding-left: 10px; border: solid 1px; }
+#test > span { border-left: solid 1px green; }
+</style>
+<script>
+
+let host = document.querySelector('#test > span');
+let shadowRoot = host.attachShadow({mode: 'closed'});
+shadowRoot.innerHTML = '<b><br></b>';
+
+function markup(node) { return node.innerHTML.replace(/</g, '&lt;'); }
+
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('')
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+shadowRoot.innerHTML = 'hi';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+shadowRoot.innerHTML = '<slot></slot>';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)');
+shouldNotBe('range.startContainer', 'null');
+shouldBe('range.startContainer', 'host.querySelector("i")');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldNotBe('range.startContainer', 'null');
+shouldBe('range.startContainer', 'host.querySelector("i")');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+host.innerHTML = 'hi';
+shadowRoot.innerHTML = '<slot></slot>';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host.firstChild');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host.firstChild');
+shouldBe('range.startOffset', '2');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+host.innerHTML = 'bye';
+shadowRoot.innerHTML = '<span id="inner-host"></span>';
+let innerHost = shadowRoot.getElementById('inner-host');
+let innerShadowRoot = innerHost.attachShadow({mode: 'closed'});
+innerShadowRoot.innerHTML = 'hi';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 1, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+shadowRoot.innerHTML = '<span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;">hi</span>';
+innerHost = shadowRoot.getElementById('inner-host');
+innerShadowRoot = innerHost.attachShadow({mode: 'closed'});
+innerShadowRoot.innerHTML = '<slot></slot>';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 15, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+shadowRoot.innerHTML = '<span id="inner-host" style="margin-left: 10px; border-left: solid 1px blue;"><slot></slot></span>';
+innerHost = shadowRoot.getElementById('inner-host');
+innerShadowRoot = innerHost.attachShadow({mode: 'closed'});
+innerShadowRoot.innerHTML = '<slot></slot>';
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 15, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host.firstChild');
+shouldBe('range.startOffset', '0');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+debug('');
+debug(`host: ${markup(host)}, shadowRoot: ${markup(shadowRoot)}, innerShadowRoot: ${markup(innerShadowRoot)}`);
+evalAndLog('range = document.caretRangeFromPoint(host.parentNode.offsetLeft + 100, host.parentNode.offsetTop + 1)');
+shouldBe('range.startContainer', 'host.firstChild');
+shouldBe('range.startOffset', '3');
+shouldBe('range.startContainer', 'range.endContainer');
+shouldBe('range.startOffset', 'range.endOffset');
+
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (209485 => 209486)


--- trunk/Source/WebCore/ChangeLog	2016-12-07 22:51:21 UTC (rev 209485)
+++ trunk/Source/WebCore/ChangeLog	2016-12-07 22:51:54 UTC (rev 209486)
@@ -1,3 +1,22 @@
+2016-12-07  Ryosuke Niwa  <rn...@webkit.org>
+
+        document.caretRangeFromPoint doesn't retarget the resultant Range correctly.
+        https://bugs.webkit.org/show_bug.cgi?id=165146
+
+        Reviewed by Sam Weinig.
+
+        The bug was caused by caretRangeFromPoint not retargeting the resultant Range correctly.
+        Namely, it's possible for RenderObject::positionForPoint to move across shadow boundary
+        even if node was identically equal to ancestorInThisScope(node).
+
+        Fixed the bug by directly retargeting the range's container node and its offset as done
+        for elementFromPoint in r206795.
+
+        Test: fast/shadow-dom/caret-range-from-point-in-shadow-tree.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::caretRangeFromPoint):
+
 2016-12-07  Antoine Quint  <grao...@apple.com>
 
         [Modern Media Controls] Entering fullscreen and returning to inline shows fullscreen controls

Modified: trunk/Source/WebCore/dom/Document.cpp (209485 => 209486)


--- trunk/Source/WebCore/dom/Document.cpp	2016-12-07 22:51:21 UTC (rev 209485)
+++ trunk/Source/WebCore/dom/Document.cpp	2016-12-07 22:51:54 UTC (rev 209486)
@@ -1420,22 +1420,19 @@
     if (!node)
         return nullptr;
 
-    Node* shadowAncestorNode = ancestorInThisScope(node);
-    if (shadowAncestorNode != node) {
-        unsigned offset = shadowAncestorNode->computeNodeIndex();
-        ContainerNode* container = shadowAncestorNode->parentNode();
-        return Range::create(*this, container, offset, container, offset);
-    }
-
     RenderObject* renderer = node->renderer();
     if (!renderer)
         return nullptr;
-    VisiblePosition visiblePosition = renderer->positionForPoint(localPoint, nullptr);
-    if (visiblePosition.isNull())
+    Position rangeCompliantPosition = renderer->positionForPoint(localPoint, nullptr).deepEquivalent().parentAnchoredEquivalent();
+    if (rangeCompliantPosition.isNull())
         return nullptr;
 
-    Position rangeCompliantPosition = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
-    return Range::create(*this, rangeCompliantPosition, rangeCompliantPosition);
+    unsigned offset = rangeCompliantPosition.offsetInContainerNode();
+    node = &retargetToScope(*rangeCompliantPosition.containerNode());
+    if (node != rangeCompliantPosition.containerNode())
+        offset = 0;
+
+    return Range::create(*this, node, offset, node, offset);
 }
 
 Element* Document::scrollingElement()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to