Title: [219961] trunk
Revision
219961
Author
[email protected]
Date
2017-07-26 13:51:26 -0700 (Wed, 26 Jul 2017)

Log Message

Implement document.elementsFromPoint
https://bugs.webkit.org/show_bug.cgi?id=153137

Patch by Ali Juma <[email protected]> on 2017-07-26
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

Add tests from upstream pull request https://github.com/w3c/web-platform-tests/pull/6568.

* web-platform-tests/cssom-view/elementsFromPoint-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-iframes-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-iframes.html: Added.
* web-platform-tests/cssom-view/elementsFromPoint-invalid-cases-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html: Added.
* web-platform-tests/cssom-view/elementsFromPoint-shadowroot-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html: Added.
* web-platform-tests/cssom-view/elementsFromPoint-simple-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-simple.html: Added.
* web-platform-tests/cssom-view/elementsFromPoint-svg-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-svg.html: Added.
* web-platform-tests/cssom-view/elementsFromPoint-table-expected.txt: Added.
* web-platform-tests/cssom-view/elementsFromPoint-table.html: Added.
* web-platform-tests/cssom-view/negativeMargins-expected.txt:
* web-platform-tests/cssom-view/resources/elementsFromPoint.js: Added.
(nodeToString.prototype.else):
(nodeListToString):
(assertElementsFromPoint):
(checkElementsFromPointFourCorners):
* web-platform-tests/cssom-view/resources/iframe1.html: Added.
* web-platform-tests/cssom-view/resources/iframe2.html: Added.

Source/WebCore:

This ports Blink's implementation of elementsFromPoint, from the
following patches by Philip Rogers ([email protected]):
-https://src.chromium.org/viewvc/blink?revision=190686&view=revision
-https://src.chromium.org/viewvc/blink?revision=191240&view=revision
-https://src.chromium.org/viewvc/blink?revision=199214&view=revision

Tests: imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes.html
       imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html
       imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html
       imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple.html
       imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg.html
       imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table.html

* dom/DocumentOrShadowRoot.idl:
* dom/TreeScope.cpp:
(WebCore::absolutePointIfNotClipped):
(WebCore::TreeScope::nodeFromPoint):
(WebCore::TreeScope::elementFromPoint):
(WebCore::TreeScope::elementsFromPoint):
* dom/TreeScope.h:
* page/EventHandler.cpp:
(WebCore::EventHandler::hitTestResultAtPoint):
* rendering/EllipsisBox.cpp:
(WebCore::EllipsisBox::nodeAtPoint):
* rendering/HitTestRequest.h:
(WebCore::HitTestRequest::HitTestRequest):
(WebCore::HitTestRequest::resultIsElementList):
(WebCore::HitTestRequest::includesAllElementsUnderPoint):
* rendering/HitTestResult.cpp:
(WebCore::HitTestResult::HitTestResult):
(WebCore::HitTestResult::operator=):
(WebCore::HitTestResult::addNodeToListBasedTestResult):
(WebCore::HitTestResult::append):
(WebCore::HitTestResult::listBasedTestResult):
(WebCore::HitTestResult::mutableListBasedTestResult):
(WebCore::HitTestResult::addNodeToRectBasedTestResult): Deleted.
(WebCore::HitTestResult::rectBasedTestResult): Deleted.
(WebCore::HitTestResult::mutableRectBasedTestResult): Deleted.
* rendering/HitTestResult.h:
* rendering/InlineFlowBox.cpp:
(WebCore::InlineFlowBox::nodeAtPoint):
* rendering/InlineTextBox.cpp:
(WebCore::InlineTextBox::nodeAtPoint):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::nodeAtPoint):
* rendering/RenderBox.cpp:
(WebCore::RenderBox::nodeAtPoint):
* rendering/RenderImage.cpp:
(WebCore::RenderImage::nodeAtPoint):
* rendering/RenderInline.cpp:
(WebCore::RenderInline::hitTestCulledInline):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hitTestFixedLayersInNamedFlows):
(WebCore::RenderLayer::hitTestLayer):
(WebCore::RenderLayer::hitTestContents):
(WebCore::RenderLayer::hitTestList):
(WebCore::RenderLayer::calculateClipRects):
* rendering/RenderTable.cpp:
(WebCore::RenderTable::nodeAtPoint):
* rendering/RenderTableSection.cpp:
(WebCore::RenderTableSection::nodeAtPoint):
* rendering/RenderWidget.cpp:
(WebCore::RenderWidget::nodeAtPoint):
* rendering/SimpleLineLayoutFunctions.cpp:
(WebCore::SimpleLineLayout::hitTestFlow):
* rendering/svg/RenderSVGContainer.cpp:
(WebCore::RenderSVGContainer::nodeAtFloatPoint):
* rendering/svg/RenderSVGImage.cpp:
(WebCore::RenderSVGImage::nodeAtFloatPoint):
* rendering/svg/RenderSVGRoot.cpp:
(WebCore::RenderSVGRoot::nodeAtPoint):
* rendering/svg/RenderSVGShape.cpp:
(WebCore::RenderSVGShape::nodeAtFloatPoint):
* rendering/svg/SVGInlineTextBox.cpp:
(WebCore::SVGInlineTextBox::nodeAtPoint):
* testing/Internals.cpp:
(WebCore::Internals::nodesFromRect):

LayoutTests:

* TestExpectations: Unskip a test.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (219960 => 219961)


--- trunk/LayoutTests/ChangeLog	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/LayoutTests/ChangeLog	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1,3 +1,12 @@
+2017-07-26  Ali Juma  <[email protected]>
+
+        Implement document.elementsFromPoint
+        https://bugs.webkit.org/show_bug.cgi?id=153137
+
+        Reviewed by Simon Fraser.
+
+        * TestExpectations: Unskip a test.
+
 2017-07-26  Brian Burg  <[email protected]>
 
         Remove WEB_TIMING feature flag

Modified: trunk/LayoutTests/TestExpectations (219960 => 219961)


--- trunk/LayoutTests/TestExpectations	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/LayoutTests/TestExpectations	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1347,7 +1347,6 @@
 fast/forms/range/range-remove-on-drag.html [ Skip ]
 
 # CSSOM View module
-webkit.org/b/153137 imported/w3c/web-platform-tests/cssom-view/elementsFromPoint.html [ Skip ]
 webkit.org/b/5991 imported/w3c/web-platform-tests/cssom-view/scrollingElement.html [ Skip ]
 
 # FileAPI

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (219960 => 219961)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1,3 +1,34 @@
+2017-07-26  Ali Juma  <[email protected]>
+
+        Implement document.elementsFromPoint
+        https://bugs.webkit.org/show_bug.cgi?id=153137
+
+        Reviewed by Simon Fraser.
+
+        Add tests from upstream pull request https://github.com/w3c/web-platform-tests/pull/6568.
+
+        * web-platform-tests/cssom-view/elementsFromPoint-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-iframes-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-iframes.html: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-invalid-cases-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-shadowroot-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-simple-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-simple.html: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-svg-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-svg.html: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-table-expected.txt: Added.
+        * web-platform-tests/cssom-view/elementsFromPoint-table.html: Added.
+        * web-platform-tests/cssom-view/negativeMargins-expected.txt:
+        * web-platform-tests/cssom-view/resources/elementsFromPoint.js: Added.
+        (nodeToString.prototype.else):
+        (nodeListToString):
+        (assertElementsFromPoint):
+        (checkElementsFromPointFourCorners):
+        * web-platform-tests/cssom-view/resources/iframe1.html: Added.
+        * web-platform-tests/cssom-view/resources/iframe2.html: Added.
+
 2017-07-11  Frederic Wang  <[email protected]>
 
         Add attribute allow-top-navigation-by-user-activation to iframe sandbox

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,17 @@
+ 
+ 
+    
+Hello!
+ 
+Another teal
+
+PASS Negative co-ordinates 
+PASS co-ordinates larger than the viewport 
+PASS co-ordinates larger than the viewport from in iframe 
+PASS Return first element that is the target for hit testing 
+PASS First element to get mouse events with pointer-events css 
+PASS SVG element at x,y 
+PASS transformed element at x,y 
+PASS no hit target at x,y 
+PASS No viewport available 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,5 @@
+ 
+
+PASS elementsFromPoint on the root document for points in iframe elements 
+PASS elementsFromPoint on inner documents 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<script>
+var loadedFrameCount = 0;
+var t1 = async_test('elementsFromPoint on the root document for points in iframe elements');
+var t2 = async_test('elementsFromPoint on inner documents');
+
+function onFrameLoaded() {
+    loadedFrameCount++;
+    if (loadedFrameCount < 2)
+        return;
+
+    var body = document.body;
+    var html = document.documentElement;
+    var iframe = document.getElementById('iframe');
+    var scrollableIframe = document.getElementById('scrollableIframe');
+    t1.step(function() {
+        checkElementsFromPointFourCorners('document', 'iframe',
+            [iframe, body,  html],
+            [iframe, body, html],
+            [iframe, body, html],
+            [scrollableIframe, iframe, body, html]);
+
+        checkElementsFromPointFourCorners('document', 'scrollableIframe',
+            [scrollableIframe, iframe, body, html],
+            [scrollableIframe, iframe, body, html],
+            [scrollableIframe, iframe, body, html],
+            [scrollableIframe, iframe, body, html]);
+    });
+    t1.done();
+
+    t2.step(function() {
+        var iframeDocument = document.getElementById('iframe').contentDocument;
+        var iframeRoot = iframeDocument.documentElement;
+        var iframeBody = iframeDocument.body;
+        var iframeDiv = iframeDocument.getElementById('div');
+        checkElementsFromPointFourCorners('document.getElementById(\'iframe\').contentDocument', 'div',
+            [iframeDiv, iframeBody, iframeRoot],
+            [iframeDiv, iframeBody, iframeRoot],
+            [iframeDiv, iframeBody, iframeRoot],
+            [iframeDiv, iframeBody, iframeRoot]);
+
+        var iframeDocument2 = document.getElementById('scrollableIframe').contentDocument;
+        var iframeRoot2 = iframeDocument2.documentElement;
+        var iframeBody2 = iframeDocument2.body;
+        var iframeSmallDiv = iframeDocument2.getElementById('small');
+        var iframeBigDiv = iframeDocument2.getElementById('big');
+        checkElementsFromPointFourCorners('document.getElementById(\'scrollableIframe\').contentDocument', 'big',
+            [iframeSmallDiv, iframeBigDiv, iframeBody2, iframeRoot2],
+            [iframeBigDiv, iframeBody2, iframeRoot2],
+            [],
+            []);
+    });
+    t2.done();
+}
+</script>
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+body {
+    height: 500px;
+}
+#iframe {
+    width: 200px;
+    height: 200px;
+}
+#scrollableIframe {
+    position: absolute;
+    top: 0;
+    left: 0;
+    transform: translate(50px, 50px);
+    width: 150px;
+    height: 150px;
+    overflow-y: scroll;
+    overflow-x: scroll;
+}
+</style>
+<iframe id="iframe" src=""
+<iframe id="scrollableIframe" src=""

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,5 @@
+
+PASS The root element is the last element returned for otherwise empty queries within the viewport 
+PASS The root element is the last element returned for valid queries 
+PASS An empty sequence is returned for queries outside the viewport 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<style>
+html {
+    overflow-y: scroll;
+    overflow-x: scroll;
+}
+html, body {
+    margin: 0;
+    padding: 0;
+}
+body {
+    width: 100%;
+    height: 100%;
+}
+#simpleDiv {
+    width: 200px;
+    height: 200px;
+    background-color: rgba(0,255,0,0.5);
+}
+#beyondTopLeft {
+    position: absolute;
+    transform: translate3d(-100px, -100px, 10px);
+    left: 0;
+    top: 0;
+    width: 100px;
+    height: 100px;
+    background-color: rgba(0,0,0,0.1);
+}
+</style>
+<body>
+<div id="beyondTopLeft"></div>
+<div id="simpleDiv"></div>
+<script>
+test(function() {
+    assertElementsFromPoint('document', 300, 300, [document.documentElement]);
+}, "The root element is the last element returned for otherwise empty queries within the viewport");
+
+test(function() {
+    var simpleDiv = document.getElementById('simpleDiv');
+    var simpleRect = simpleDiv.getBoundingClientRect();
+    var simpleCoords = (simpleRect.right - 1) + ', ' + (simpleRect.bottom - 1);
+    assertElementsFromPoint('document', simpleRect.right - 1, simpleRect.bottom - 1,
+        [simpleDiv, document.body, document.documentElement]);
+}, "The root element is the last element returned for valid queries");
+
+test(function() {
+    assertElementsFromPoint('document', window.innerWidth + 1, window.innerHeight + 1, []);
+    assertElementsFromPoint('document', -1, -1, []);
+    assertElementsFromPoint('document', 1, -1, []);
+    assertElementsFromPoint('document', -1, 1, []);
+}, "An empty sequence is returned for queries outside the viewport");
+</script>
+</body>

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,5 @@
+ 
+
+PASS elementsFromPoint on the document root should not return elements in shadow trees 
+PASS elementsFromPoint on a shadow root should include elements in that shadow tree 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+body {
+    height: 500px;
+}
+</style>
+<body>
+<div id="host"></div>
+<div id="blockHost"></div>
+<span id="inlineBlockHost" style="display:inline-block;"></span>
+<input type="submit" id="submit">
+<script>
+function assertElementsFromPoint(doc, x, y, expected) {
+  var query = doc + '.elementsFromPoint(' + x + ',' + y + ')';
+  var sequence = eval(query);
+  assert_equals(nodeListToString(sequence), nodeListToString(expected), query);
+}
+
+function createBox(id) {
+    var div = document.createElement('div');
+    div.id = id;
+    div.style.width = '100px';
+    div.style.height = '10px';
+    return div;
+}
+
+function centerX(element) {
+    return element.offsetLeft + element.offsetWidth / 2;
+}
+
+function centerY(element) {
+    return element.offsetTop + element.offsetHeight / 2;
+}
+
+var shadowRoot = host.attachShadow({mode: 'closed'});
+var box11 = createBox('box11');
+var box12 = createBox('box12');
+var box13 = createBox('box13');
+shadowRoot.appendChild(box11);
+shadowRoot.appendChild(box12);
+shadowRoot.appendChild(box13);
+
+var nestedHost = document.createElement('div');
+var nestedShadowRoot = nestedHost.attachShadow({mode: 'closed'});
+var box21 = createBox('box21');
+var box22 = createBox('box22');
+var box23 = createBox('box23');
+nestedShadowRoot.appendChild(box21);
+nestedShadowRoot.appendChild(box22);
+nestedShadowRoot.appendChild(box23);
+
+shadowRoot.appendChild(nestedHost);
+
+var x12 = centerX(box12);
+var y12 = centerY(box12);
+var x22 = centerX(box22);
+var y22 = centerY(box22);
+
+var root3 = blockHost.attachShadow({mode: 'closed'});
+root3.appendChild(document.createTextNode('text1'));
+var root4 = inlineBlockHost.attachShadow({mode: 'closed'});
+root4.appendChild(document.createTextNode('text2'));
+
+test(function() {
+    assertElementsFromPoint('document', x22, y22, [host, document.body, document.documentElement]);
+    assertElementsFromPoint('document', centerX(blockHost), centerY(blockHost),
+        [blockHost, document.body, document.documentElement]);
+    assertElementsFromPoint('document', centerX(inlineBlockHost), centerY(inlineBlockHost),
+        [inlineBlockHost, document.body, document.documentElement]);
+    assertElementsFromPoint('document', centerX(submit), centerY(submit),
+        [submit, document.body, document.documentElement]);
+}, 'elementsFromPoint on the document root should not return elements in shadow trees');
+
+test(function() {
+    assert_not_equals(shadowRoot.elementsFromPoint(x12, y12).indexOf(box12), -1);
+    assert_not_equals(shadowRoot.elementsFromPoint(x22, y22).indexOf(nestedHost), -1);
+    assert_not_equals(nestedShadowRoot.elementsFromPoint(x22, y22).indexOf(box22), -1);
+}, 'elementsFromPoint on a shadow root should include elements in that shadow tree');
+</script>
+</body>

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,8 @@
+
+PASS elementsFromPoint for each corner of a simple div 
+PASS elementsFromPoint for each corner of a div that has a pseudo-element 
+PASS elementsFromPoint for each corner of a div that is between another div and its pseudo-element 
+PASS elementsFromPoint for each corner of a div that has a margin 
+PASS elementsFromPoint for each corner of a div with pointer-events:none 
+PASS elementsFromPoint for each corner of a div with a 3d transform 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+body {
+    height: 500px;
+}
+#simpleDiv {
+    width: 200px;
+    height: 200px;
+    background-color: rgba(0,0,255,0.5);
+}
+#divWithPseudo {
+    position: absolute;
+    left: 50px;
+    top: 50px;
+    width: 100px;
+    height: 100px;
+    background-color: rgba(255,0,0,0.5);
+}
+#divWithPseudo::before {
+    position: absolute;
+    left: 20px;
+    top: 20px;
+    width: 100px;
+    height: 100px;
+    content: "::before";
+    background-color: rgba(255,0,0,0.5);
+    z-index: 9999;
+}
+#divBetweenPseudo {
+    position: absolute;
+    left: 100px;
+    top: 100px;
+    width: 100px;
+    height: 100px;
+    background-color: rgba(0,255,0,0.5);
+}
+#withMargin {
+    margin-top: -15px;
+    width: 200px;
+    height: 200px;
+    background-color: rgba(0,0,0,0.5);
+}
+#inlineSpan {
+    float: right;
+    background-color: yellow;
+    width: 100px;
+    height: 1em;
+}
+#noPointerEvents {
+    position: absolute;
+    left: 50px;
+    top: 50px;
+    width: 100px;
+    height: 300px;
+    background-color: rgba(0,0,0,0.1);
+    pointer-events: none;
+}
+#threeD {
+    position: absolute;
+    transform: translate3d(-100px, -100px, 10px);
+    left: 140px;
+    top: 140px;
+    width: 200px;
+    height: 50px;
+    background-color: rgba(255,255,255,0.5);
+}
+</style>
+<div id="simpleDiv"></div>
+<div id="divWithPseudo"></div>
+<div id="divBetweenPseudo"></div>
+<div id="withMargin"><span id="inlineSpan"></span></div>
+<div id="noPointerEvents"></div>
+<div id="threeD"></div>
+<script>
+var body = document.body;
+var html = document.documentElement;
+test(function() {
+    checkElementsFromPointFourCorners('document', 'simpleDiv',
+        [simpleDiv, body, html],
+        [simpleDiv, body, html],
+        [withMargin, simpleDiv, body, html],
+        [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a simple div");
+
+test(function() {
+    checkElementsFromPointFourCorners('document', 'divWithPseudo',
+        [threeD, divWithPseudo, simpleDiv, body, html],
+        [threeD, divWithPseudo, simpleDiv, body, html],
+        [divWithPseudo, simpleDiv, body, html],
+        [divWithPseudo, divBetweenPseudo, divWithPseudo, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a div that has a pseudo-element");
+
+test(function() {
+    checkElementsFromPointFourCorners('document', 'divBetweenPseudo',
+        [divWithPseudo, divBetweenPseudo, divWithPseudo, simpleDiv, body, html],
+        [divBetweenPseudo, simpleDiv, body, html],
+        [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html],
+        [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a div that is between another div and its pseudo-element");
+
+test(function() {
+    checkElementsFromPointFourCorners('document', 'withMargin',
+        [withMargin, simpleDiv, body, html],
+        [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html],
+        [withMargin, body, html],
+        [withMargin, body, html]);
+}, "elementsFromPoint for each corner of a div that has a margin");
+
+test(function() {
+    checkElementsFromPointFourCorners('document', 'noPointerEvents',
+        [threeD, divWithPseudo, simpleDiv, body, html],
+        [threeD, divWithPseudo, simpleDiv, body, html],
+        [withMargin, body, html],
+        [withMargin, body, html]);
+}, "elementsFromPoint for each corner of a div with pointer-events:none");
+
+test(function() {
+    checkElementsFromPointFourCorners('document', 'threeD',
+        [threeD, simpleDiv, body, html],
+        [threeD, body, html],
+        [threeD, simpleDiv, body, html],
+        [threeD, body, html]);
+}, "elementsFromPoint for each corner of a div with a 3d transform");
+</script>

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,6 @@
+
+PASS elementsFromPoint for a point inside two rects 
+PASS elementsFromPoint for a point inside two rects that are inside a <g> 
+PASS elementsFromPoint for a point inside two images 
+PASS elementsFromPoint for a point inside transformed rects and <g> 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+#svg {
+    margin: 100px;
+    background-color: rgba(0,180,0,0.2);
+}
+rect {
+    fill: rgba(180,0,0,0.2);
+}
+#topLeftRect2NoHitTest {
+    pointer-events: none;
+}
+</style>
+<div id='sandbox'>
+    <svg id='svg' width='300' height='300'>
+        <rect id='topLeftRect1' x='5' y='5' width='90' height='90'/>
+        <rect id='topLeftRect2NoHitTest' x='10' y='10' width='80' height='80'/>
+        <rect id='topLeftRect3' x='15' y='15' width='70' height='70'/>
+
+        <g id='middleG1'>
+            <g id='middleG2'>
+                <rect id='middleRect1' x='105' y='105' width='90' height='90'/>
+                <rect id='middleRect2' x='110' y='110' width='80' height='80'/>
+            </g>
+        </g>
+
+        <g id='bottomLeftG'>
+            <image id='bottomLeftImage1' x='5' y='205' width='90' height='90' xlink:href=''/>
+            <image id='bottomLeftImage2' x='10' y='210' width='80' height='80' xlink:href=''/>
+        </g>
+
+        <g id='bottomRightG1' transform='translate(300, 300)'>
+            <g id='bottomRightG2' transform='translate(-100, -100)'>
+                <rect id='bottomRightRect1' x='5' y='5' width='90' height='90'/>
+                <rect id='bottomRightRect2' x='110' y='110' width='80' height='80' transform='translate(-100, -100)'/>
+            </g>
+        </g>
+    </svg>
+</div>
+<script>
+test(function() {
+    assertElementsFromPoint('document', 125, 125,
+        [topLeftRect3, topLeftRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two rects');
+
+test(function() {
+    assertElementsFromPoint('document', 225, 225,
+        [middleRect2, middleRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two rects that are inside a <g>');
+
+test(function() {
+    assertElementsFromPoint('document', 125, 325,
+        [bottomLeftImage2, bottomLeftImage1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two images');
+
+test(function() {
+    assertElementsFromPoint('document', 325, 325,
+        [bottomRightRect2, bottomRightRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside transformed rects and <g>');
+</script>

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table-expected.txt (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,6 @@
+
+PASS elementsFromPoint for points inside table cells 
+PASS elementsFromPoint for points between table cells 
+PASS elementsFromPoint for points inside cells in a right-to-left table 
+PASS elementsFromPoint for points inside cells in a flipped (writing-mode:vertical-lr) table 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<script src=""
+<script src=""
+<script src=""
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+#testtable {
+    margin: 100px;
+    width: 200px;
+    height: 200px;
+    background-color: rgba(0,180,0,0.2);
+}
+#testtable tr {
+    background-color: rgba(180,0,0,0.2);
+}
+#testtable td {
+    background-color: rgba(0,0,180,0.2);
+}
+.rtl {
+    direction: rtl;
+}
+.tblr {
+    writing-mode: vertical-lr;
+}
+</style>
+<div id='sandbox'>
+    <table id='testtable'>
+        <tr id='tr1'>
+            <td id='td11'></td>
+            <td id='td12'></td>
+            <td id='td13'></td>
+            <td id='td14'></td>
+        </tr>
+        <tr id='tr2'>
+            <td id='td21'></td>
+            <td id='td22'></td>
+            <td id='td23'></td>
+            <td id='td24'></td>
+        </tr>
+        <tr id='tr3'>
+            <td id='td31'></td>
+            <td id='td32'></td>
+            <td id='td33'></td>
+            <td id='td34'></td>
+        </tr>
+        <tr id='tr4'>
+            <td id='td41'></td>
+            <td id='td42'></td>
+            <td id='td43'></td>
+            <td id='td44'></td>
+        </tr>
+    </table>
+</div>
+<script>
+test(function() {
+    assertElementsFromPoint('document', 125, 125,
+        [td11, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 275, 125,
+        [td14, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 175, 175,
+        [td22, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 125, 275,
+        [td41, testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside table cells');
+
+test(function() {
+    assertElementsFromPoint('document', 100, 100,
+        [testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 199, 199,
+        [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points between table cells');
+
+testtable.setAttribute('class', 'rtl');
+test(function() {
+    assertElementsFromPoint('document', 125, 125,
+        [td14, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 275, 125,
+        [td11, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 100, 100,
+        [testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 199, 199,
+        [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside cells in a right-to-left table');
+
+testtable.setAttribute('class', 'tblr');
+test(function() {
+    assertElementsFromPoint('document', 125, 275,
+        [td14, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 275, 125,
+        [td41, testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 100, 100,
+        [testtable, sandbox, document.body, document.documentElement]);
+    assertElementsFromPoint('document', 199, 199,
+        [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside cells in a flipped (writing-mode:vertical-lr) table');
+</script>

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/negativeMargins-expected.txt (219960 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/negativeMargins-expected.txt	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/negativeMargins-expected.txt	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1,6 +1,5 @@
 Hello
 
 PASS cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins 
-FAIL cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins 1 document.elementsFromPoint is not a function. (In 'document.elementsFromPoint(outerRect.left + 1,
-                                                    outerRect.top + 1)', 'document.elementsFromPoint' is undefined)
+FAIL cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins 1 assert_array_equals: elementsFromPoint should get sequence [inner, outer, body, html] lengths differ, expected 4 got 5
 

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/elementsFromPoint.js (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/elementsFromPoint.js	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/elementsFromPoint.js	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,48 @@
+function nodeToString(node) {
+  var str = '';
+  if (node.nodeType == Node.ELEMENT_NODE) {
+    str += node.nodeName;
+    if (node.id)
+      str += '#' + node.id;
+    else if (node.class)
+      str += '.' + node.class;
+  } else if (node.nodeType == Node.TEXT_NODE) {
+    str += '\'' + node.data + '\'';
+  } else if (node.nodeType == Node.DOCUMENT_NODE) {
+    str += '#document';
+  }
+  return str;
+}
+
+function nodeListToString(nodes) {
+  var nodeString = '';
+
+  for (var i = 0; i < nodes.length; i++) {
+    var str = nodeToString(nodes[i]);
+    if (!str)
+      continue;
+    nodeString += str;
+    if (i + 1 < nodes.length)
+      nodeString += ', ';
+  }
+  return nodeString;
+}
+
+function assertElementsFromPoint(doc, x, y, expected) {
+  var query = doc + '.elementsFromPoint(' + x + ',' + y + ')';
+  var sequence = eval(query);
+  assert_equals(nodeListToString(sequence), nodeListToString(expected), query);
+}
+
+function checkElementsFromPointFourCorners(doc, element, expectedTopLeft, expectedTopRight, expectedBottomLeft, expectedBottomRight) {
+  var rect = eval(doc + '.getElementById(\'' + element + '\')').getBoundingClientRect();
+  var topLeft = {x: rect.left + 1, y: rect.top + 1};
+  var topRight = {x: rect.right - 1, y: rect.top + 1};
+  var bottomLeft = {x: rect.left + 1, y: rect.bottom - 1};
+  var bottomRight = {x: rect.right - 1, y: rect.bottom - 1};
+
+  assertElementsFromPoint(doc, topLeft.x, topLeft.y, expectedTopLeft);
+  assertElementsFromPoint(doc, topRight.x, topRight.y, expectedTopRight);
+  assertElementsFromPoint(doc, bottomLeft.x, bottomLeft.y, expectedBottomLeft);
+  assertElementsFromPoint(doc, bottomRight.x, bottomRight.y, expectedBottomRight);
+}

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe1.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe1.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe1.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+#div {
+    width: 100px;
+    height: 100px;
+    background: red;
+}
+</style>
+<div id='div'></div>
+<script>
+window._onload_ = window.parent.onFrameLoaded();
+</script>

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe2.html (0 => 219961)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe2.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/cssom-view/resources/iframe2.html	2017-07-26 20:51:26 UTC (rev 219961)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<style>
+html, body {
+    margin: 0;
+    padding: 0;
+}
+#big {
+    width: 125px;
+    height: 500px;
+    background: blue;
+}
+#small {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100px;
+    height: 100px;
+    background: green;
+}
+</style>
+<div id='big'></div>
+<div id='small'></div>
+<script>
+window._onload_ = window.parent.onFrameLoaded();
+</script>

Modified: trunk/Source/WebCore/ChangeLog (219960 => 219961)


--- trunk/Source/WebCore/ChangeLog	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/ChangeLog	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1,3 +1,88 @@
+2017-07-26  Ali Juma  <[email protected]>
+
+        Implement document.elementsFromPoint
+        https://bugs.webkit.org/show_bug.cgi?id=153137
+
+        Reviewed by Simon Fraser.
+
+        This ports Blink's implementation of elementsFromPoint, from the
+        following patches by Philip Rogers ([email protected]):
+        -https://src.chromium.org/viewvc/blink?revision=190686&view=revision
+        -https://src.chromium.org/viewvc/blink?revision=191240&view=revision
+        -https://src.chromium.org/viewvc/blink?revision=199214&view=revision
+
+        Tests: imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-iframes.html
+               imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-invalid-cases.html
+               imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-shadowroot.html
+               imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-simple.html
+               imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-svg.html
+               imported/w3c/web-platform-tests/cssom-view/elementsFromPoint-table.html
+
+        * dom/DocumentOrShadowRoot.idl:
+        * dom/TreeScope.cpp:
+        (WebCore::absolutePointIfNotClipped):
+        (WebCore::TreeScope::nodeFromPoint):
+        (WebCore::TreeScope::elementFromPoint):
+        (WebCore::TreeScope::elementsFromPoint):
+        * dom/TreeScope.h:
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::hitTestResultAtPoint):
+        * rendering/EllipsisBox.cpp:
+        (WebCore::EllipsisBox::nodeAtPoint):
+        * rendering/HitTestRequest.h:
+        (WebCore::HitTestRequest::HitTestRequest):
+        (WebCore::HitTestRequest::resultIsElementList):
+        (WebCore::HitTestRequest::includesAllElementsUnderPoint):
+        * rendering/HitTestResult.cpp:
+        (WebCore::HitTestResult::HitTestResult):
+        (WebCore::HitTestResult::operator=):
+        (WebCore::HitTestResult::addNodeToListBasedTestResult):
+        (WebCore::HitTestResult::append):
+        (WebCore::HitTestResult::listBasedTestResult):
+        (WebCore::HitTestResult::mutableListBasedTestResult):
+        (WebCore::HitTestResult::addNodeToRectBasedTestResult): Deleted.
+        (WebCore::HitTestResult::rectBasedTestResult): Deleted.
+        (WebCore::HitTestResult::mutableRectBasedTestResult): Deleted.
+        * rendering/HitTestResult.h:
+        * rendering/InlineFlowBox.cpp:
+        (WebCore::InlineFlowBox::nodeAtPoint):
+        * rendering/InlineTextBox.cpp:
+        (WebCore::InlineTextBox::nodeAtPoint):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::nodeAtPoint):
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::nodeAtPoint):
+        * rendering/RenderImage.cpp:
+        (WebCore::RenderImage::nodeAtPoint):
+        * rendering/RenderInline.cpp:
+        (WebCore::RenderInline::hitTestCulledInline):
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::hitTestFixedLayersInNamedFlows):
+        (WebCore::RenderLayer::hitTestLayer):
+        (WebCore::RenderLayer::hitTestContents):
+        (WebCore::RenderLayer::hitTestList):
+        (WebCore::RenderLayer::calculateClipRects):
+        * rendering/RenderTable.cpp:
+        (WebCore::RenderTable::nodeAtPoint):
+        * rendering/RenderTableSection.cpp:
+        (WebCore::RenderTableSection::nodeAtPoint):
+        * rendering/RenderWidget.cpp:
+        (WebCore::RenderWidget::nodeAtPoint):
+        * rendering/SimpleLineLayoutFunctions.cpp:
+        (WebCore::SimpleLineLayout::hitTestFlow):
+        * rendering/svg/RenderSVGContainer.cpp:
+        (WebCore::RenderSVGContainer::nodeAtFloatPoint):
+        * rendering/svg/RenderSVGImage.cpp:
+        (WebCore::RenderSVGImage::nodeAtFloatPoint):
+        * rendering/svg/RenderSVGRoot.cpp:
+        (WebCore::RenderSVGRoot::nodeAtPoint):
+        * rendering/svg/RenderSVGShape.cpp:
+        (WebCore::RenderSVGShape::nodeAtFloatPoint):
+        * rendering/svg/SVGInlineTextBox.cpp:
+        (WebCore::SVGInlineTextBox::nodeAtPoint):
+        * testing/Internals.cpp:
+        (WebCore::Internals::nodesFromRect):
+
 2017-07-26  Charlie Turner  <[email protected]>
 
         [GStreamer] Review WebKitWebSource after r219252.

Modified: trunk/Source/WebCore/dom/DocumentOrShadowRoot.idl (219960 => 219961)


--- trunk/Source/WebCore/dom/DocumentOrShadowRoot.idl	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/dom/DocumentOrShadowRoot.idl	2017-07-26 20:51:26 UTC (rev 219961)
@@ -31,7 +31,7 @@
     // Extensions from Shadow DOM API (https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin).
     // DOMSelection? getSelection(); // FIXME: We currently have this on Document only.
     Element? elementFromPoint(double x, double y);
-    // sequence<Element> elementsFromPoint(double x, double y); // FIXME: Implement this.
+    sequence<Element> elementsFromPoint(double x, double y);
     // CaretPosition? caretPositionFromPoint(double x, double y); // FIXME: Implement this.
     readonly attribute Element? activeElement;
     // readonly attribute StyleSheetList styleSheets; // FIXME: Implement this.

Modified: trunk/Source/WebCore/dom/TreeScope.cpp (219960 => 219961)


--- trunk/Source/WebCore/dom/TreeScope.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/dom/TreeScope.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -42,6 +42,7 @@
 #include "NodeRareData.h"
 #include "Page.h"
 #include "PointerLockController.h"
+#include "PseudoElement.h"
 #include "RenderView.h"
 #include "RuntimeEnabledFeatures.h"
 #include "Settings.h"
@@ -295,39 +296,46 @@
     return m_labelsByForAttribute->getElementByLabelForAttribute(*forAttributeValue.impl(), *this);
 }
 
-Node* TreeScope::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint)
+static std::optional<LayoutPoint> absolutePointIfNotClipped(Document& document, const LayoutPoint& clientPoint)
 {
-    auto* frame = documentScope().frame();
-    auto* view = documentScope().view();
+    auto* frame = document.frame();
+    auto* view = document.view();
     if (!frame || !view)
-        return nullptr;
+        return std::nullopt;
 
-    LayoutPoint absolutePoint;
     if (frame->settings().visualViewportEnabled()) {
-        documentScope().updateLayout();
+        document.updateLayout();
         FloatPoint layoutViewportPoint = view->clientToLayoutViewportPoint(clientPoint);
         FloatRect layoutViewportBounds({ }, view->layoutViewportRect().size());
         if (!layoutViewportBounds.contains(layoutViewportPoint))
-            return nullptr;
-        absolutePoint = LayoutPoint(view->layoutViewportToAbsolutePoint(layoutViewportPoint));
-    } else {
-        float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor();
+            return std::nullopt;
+        return LayoutPoint(view->layoutViewportToAbsolutePoint(layoutViewportPoint));
+    }
 
-        absolutePoint = clientPoint;
-        absolutePoint.scale(scaleFactor);
-        absolutePoint.moveBy(view->contentsScrollPosition());
+    float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor();
 
-        LayoutRect visibleRect;
+    LayoutPoint absolutePoint = clientPoint;
+    absolutePoint.scale(scaleFactor);
+    absolutePoint.moveBy(view->contentsScrollPosition());
+
+    LayoutRect visibleRect;
 #if PLATFORM(IOS)
-        visibleRect = view->unobscuredContentRect();
+    visibleRect = view->unobscuredContentRect();
 #else
-        visibleRect = view->visibleContentRect();
+    visibleRect = view->visibleContentRect();
 #endif
-        if (!visibleRect.contains(absolutePoint))
-            return nullptr;
-    }
+    if (visibleRect.contains(absolutePoint))
+        return absolutePoint;
+    return std::nullopt;
+}
 
-    HitTestResult result(absolutePoint);
+Node* TreeScope::nodeFromPoint(const LayoutPoint& clientPoint, LayoutPoint* localPoint)
+{
+    auto absolutePoint = absolutePointIfNotClipped(documentScope(), clientPoint);
+    if (!absolutePoint)
+        return nullptr;
+
+    HitTestResult result(absolutePoint.value());
     documentScope().renderView()->hitTest(HitTestRequest(), result);
 
     if (localPoint)
@@ -336,13 +344,13 @@
     return result.innerNode();
 }
 
-Element* TreeScope::elementFromPoint(double x, double y)
+RefPtr<Element> TreeScope::elementFromPoint(double clientX, double clientY)
 {
     Document& document = documentScope();
     if (!document.hasLivingRenderTree())
         return nullptr;
 
-    Node* node = nodeFromPoint(LayoutPoint(x, y), nullptr);
+    Node* node = nodeFromPoint(LayoutPoint(clientX, clientY), nullptr);
     if (!node)
         return nullptr;
 
@@ -357,6 +365,62 @@
     return downcast<Element>(node);
 }
 
+Vector<RefPtr<Element>> TreeScope::elementsFromPoint(double clientX, double clientY)
+{
+    Vector<RefPtr<Element>> elements;
+
+    Document& document = documentScope();
+    if (!document.hasLivingRenderTree())
+        return elements;
+
+    auto absolutePoint = absolutePointIfNotClipped(document, LayoutPoint(clientX, clientY));
+    if (!absolutePoint)
+        return elements;
+
+    HitTestRequest request(HitTestRequest::ReadOnly
+        | HitTestRequest::Active
+        | HitTestRequest::DisallowUserAgentShadowContent
+        | HitTestRequest::CollectMultipleElements
+        | HitTestRequest::IncludeAllElementsUnderPoint);
+    HitTestResult result(absolutePoint.value());
+    documentScope().renderView()->hitTest(request, result);
+
+    Node* lastNode = nullptr;
+    for (auto listBasedNode : result.listBasedTestResult()) {
+        Node* node = listBasedNode.get();
+        node = &retargetToScope(*node);
+        while (!is<Element>(*node)) {
+            node = node->parentInComposedTree();
+            if (!node)
+                break;
+            node = &retargetToScope(*node);
+        }
+
+        if (!node)
+            continue;
+
+        if (is<PseudoElement>(node))
+            node = downcast<PseudoElement>(*node).hostElement();
+
+        // Prune duplicate entries. A pseudo ::before content above its parent
+        // node should only result in one entry.
+        if (node == lastNode)
+            continue;
+
+        elements.append(downcast<Element>(node));
+        lastNode = node;
+    }
+
+    if (m_rootNode.isDocumentNode()) {
+        if (Element* rootElement = downcast<Document>(m_rootNode).documentElement()) {
+            if (elements.isEmpty() || elements.last() != rootElement)
+                elements.append(rootElement);
+        }
+    }
+
+    return elements;
+}
+
 Element* TreeScope::findAnchor(const String& name)
 {
     if (name.isEmpty())

Modified: trunk/Source/WebCore/dom/TreeScope.h (219960 => 219961)


--- trunk/Source/WebCore/dom/TreeScope.h	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/dom/TreeScope.h	2017-07-26 20:51:26 UTC (rev 219961)
@@ -29,6 +29,7 @@
 #include "DocumentOrderedMap.h"
 #include <memory>
 #include <wtf/Forward.h>
+#include <wtf/Vector.h>
 #include <wtf/text/AtomicString.h>
 
 namespace WebCore {
@@ -87,7 +88,8 @@
     void removeLabel(const AtomicStringImpl& forAttributeValue, HTMLLabelElement&);
     HTMLLabelElement* labelElementForId(const AtomicString& forAttributeValue);
 
-    WEBCORE_EXPORT Element* elementFromPoint(double x, double y);
+    WEBCORE_EXPORT RefPtr<Element> elementFromPoint(double clientX, double clientY);
+    WEBCORE_EXPORT Vector<RefPtr<Element>> elementsFromPoint(double clientX, double clientY);
 
     // Find first anchor with the given name.
     // First searches for an element with the given ID, but if that fails, then looks

Modified: trunk/Source/WebCore/page/EventHandler.cpp (219960 => 219961)


--- trunk/Source/WebCore/page/EventHandler.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1122,6 +1122,8 @@
 
 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const
 {
+    ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty());
+
     Ref<Frame> protectedFrame(m_frame);
 
     // We always send hitTestResultAtPoint to the main frame if we have one,

Modified: trunk/Source/WebCore/rendering/EllipsisBox.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/EllipsisBox.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/EllipsisBox.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -161,7 +161,7 @@
     LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height));
     if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) {
         blockFlow().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
-        if (!result.addNodeToRectBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect))
+        if (result.addNodeToListBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
             return true;
     }
 

Modified: trunk/Source/WebCore/rendering/HitTestRequest.h (219960 => 219961)


--- trunk/Source/WebCore/rendering/HitTestRequest.h	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/HitTestRequest.h	2017-07-26 20:51:26 UTC (rev 219961)
@@ -22,6 +22,8 @@
 
 #pragma once
 
+#include <wtf/Assertions.h>
+
 namespace WebCore {
 
 class HitTestRequest {
@@ -38,7 +40,11 @@
         AllowFrameScrollbars = 1 << 9,
         AllowChildFrameContent = 1 << 10,
         ChildFrameHitTest = 1 << 11,
-        AccessibilityHitTest = 1 << 12
+        AccessibilityHitTest = 1 << 12,
+        // Collect a list of nodes instead of just one. Used for elementsFromPoint and rect-based tests.
+        CollectMultipleElements = 1 << 13,
+        // When using list-based testing, continue hit testing even after a hit has been found.
+        IncludeAllElementsUnderPoint = 1 << 14
     };
 
     typedef unsigned HitTestRequestType;
@@ -46,6 +52,7 @@
     HitTestRequest(HitTestRequestType requestType = ReadOnly | Active | DisallowUserAgentShadowContent)
         : m_requestType(requestType)
     {
+        ASSERT(!(requestType & IncludeAllElementsUnderPoint) || (requestType & CollectMultipleElements));
     }
 
     bool readOnly() const { return m_requestType & ReadOnly; }
@@ -60,6 +67,8 @@
     bool allowsFrameScrollbars() const { return m_requestType & AllowFrameScrollbars; }
     bool allowsChildFrameContent() const { return m_requestType & AllowChildFrameContent; }
     bool isChildFrameHitTest() const { return m_requestType & ChildFrameHitTest; }
+    bool resultIsElementList() const { return m_requestType & CollectMultipleElements; }
+    bool includesAllElementsUnderPoint() const { return m_requestType & IncludeAllElementsUnderPoint; }
 
     // Convenience functions
     bool touchMove() const { return move() && touchEvent(); }

Modified: trunk/Source/WebCore/rendering/HitTestResult.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/HitTestResult.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/HitTestResult.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -95,8 +95,8 @@
     , m_scrollbar(other.scrollbar())
     , m_isOverWidget(other.isOverWidget())
 {
-    // Only copy the NodeSet in case of rect hit test.
-    m_rectBasedTestResult = other.m_rectBasedTestResult ? std::make_unique<NodeSet>(*other.m_rectBasedTestResult) : nullptr;
+    // Only copy the NodeSet in case of list hit test.
+    m_listBasedTestResult = other.m_listBasedTestResult ? std::make_unique<NodeSet>(*other.m_listBasedTestResult) : nullptr;
 }
 
 HitTestResult::~HitTestResult()
@@ -114,8 +114,8 @@
     m_scrollbar = other.scrollbar();
     m_isOverWidget = other.isOverWidget();
 
-    // Only copy the NodeSet in case of rect hit test.
-    m_rectBasedTestResult = other.m_rectBasedTestResult ? std::make_unique<NodeSet>(*other.m_rectBasedTestResult) : nullptr;
+    // Only copy the NodeSet in case of list hit test.
+    m_listBasedTestResult = other.m_listBasedTestResult ? std::make_unique<NodeSet>(*other.m_listBasedTestResult) : nullptr;
 
     return *this;
 }
@@ -656,49 +656,55 @@
     return m_innerNonSharedNode->hasEditableStyle();
 }
 
-bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
+HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const LayoutRect& rect)
 {
-    // If it is not a rect-based hit test, this method has to be no-op.
-    // Return false, so the hit test stops.
-    if (!isRectBasedTest())
-        return false;
+    // If it is not a list-based hit test, this method has to be no-op.
+    if (!request.resultIsElementList()) {
+        ASSERT(!isRectBasedTest());
+        return HitTestProgress::Stop;
+    }
 
-    // If node is null, return true so the hit test can continue.
     if (!node)
-        return true;
+        return HitTestProgress::Continue;
 
     if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
         node = node->document().ancestorNodeInThisScope(node);
 
-    mutableRectBasedTestResult().add(node);
+    mutableListBasedTestResult().add(node);
 
+    if (request.includesAllElementsUnderPoint())
+        return HitTestProgress::Continue;
+
     bool regionFilled = rect.contains(locationInContainer.boundingBox());
-    return !regionFilled;
+    return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
 }
 
-bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
+HitTestProgress HitTestResult::addNodeToListBasedTestResult(Node* node, const HitTestRequest& request, const HitTestLocation& locationInContainer, const FloatRect& rect)
 {
-    // If it is not a rect-based hit test, this method has to be no-op.
-    // Return false, so the hit test stops.
-    if (!isRectBasedTest())
-        return false;
+    // If it is not a list-based hit test, this method has to be no-op.
+    if (!request.resultIsElementList()) {
+        ASSERT(!isRectBasedTest());
+        return HitTestProgress::Stop;
+    }
 
-    // If node is null, return true so the hit test can continue.
     if (!node)
-        return true;
+        return HitTestProgress::Continue;
 
     if (request.disallowsUserAgentShadowContent() && node->isInUserAgentShadowTree())
         node = node->document().ancestorNodeInThisScope(node);
 
-    mutableRectBasedTestResult().add(node);
+    mutableListBasedTestResult().add(node);
 
+    if (request.includesAllElementsUnderPoint())
+        return HitTestProgress::Continue;
+
     bool regionFilled = rect.contains(locationInContainer.boundingBox());
-    return !regionFilled;
+    return regionFilled ? HitTestProgress::Stop : HitTestProgress::Continue;
 }
 
-void HitTestResult::append(const HitTestResult& other)
+void HitTestResult::append(const HitTestResult& other, const HitTestRequest& request)
 {
-    ASSERT(isRectBasedTest() && other.isRectBasedTest());
+    ASSERT_UNUSED(request, request.resultIsElementList());
 
     if (!m_innerNode && other.innerNode()) {
         m_innerNode = other.innerNode();
@@ -710,25 +716,25 @@
         m_isOverWidget = other.isOverWidget();
     }
 
-    if (other.m_rectBasedTestResult) {
-        NodeSet& set = mutableRectBasedTestResult();
-        for (NodeSet::const_iterator it = other.m_rectBasedTestResult->begin(), last = other.m_rectBasedTestResult->end(); it != last; ++it)
-            set.add(it->get());
+    if (other.m_listBasedTestResult) {
+        NodeSet& set = mutableListBasedTestResult();
+        for (auto node : *other.m_listBasedTestResult)
+            set.add(node.get());
     }
 }
 
-const HitTestResult::NodeSet& HitTestResult::rectBasedTestResult() const
+const HitTestResult::NodeSet& HitTestResult::listBasedTestResult() const
 {
-    if (!m_rectBasedTestResult)
-        m_rectBasedTestResult = std::make_unique<NodeSet>();
-    return *m_rectBasedTestResult;
+    if (!m_listBasedTestResult)
+        m_listBasedTestResult = std::make_unique<NodeSet>();
+    return *m_listBasedTestResult;
 }
 
-HitTestResult::NodeSet& HitTestResult::mutableRectBasedTestResult()
+HitTestResult::NodeSet& HitTestResult::mutableListBasedTestResult()
 {
-    if (!m_rectBasedTestResult)
-        m_rectBasedTestResult = std::make_unique<NodeSet>();
-    return *m_rectBasedTestResult;
+    if (!m_listBasedTestResult)
+        m_listBasedTestResult = std::make_unique<NodeSet>();
+    return *m_listBasedTestResult;
 }
 
 Vector<String> HitTestResult::dictationAlternatives() const

Modified: trunk/Source/WebCore/rendering/HitTestResult.h (219960 => 219961)


--- trunk/Source/WebCore/rendering/HitTestResult.h	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/HitTestResult.h	2017-07-26 20:51:26 UTC (rev 219961)
@@ -40,6 +40,8 @@
 class Scrollbar;
 class URL;
 
+enum class HitTestProgress { Stop, Continue };
+
 class HitTestResult {
 public:
     typedef ListHashSet<RefPtr<Node>> NodeSet;
@@ -131,16 +133,14 @@
     WEBCORE_EXPORT bool isOverTextInsideFormControlElement() const;
     WEBCORE_EXPORT bool allowsCopy() const;
 
-    // Returns true if it is rect-based hit test and needs to continue until the rect is fully
-    // enclosed by the boundaries of a node.
-    bool addNodeToRectBasedTestResult(Node*, const HitTestRequest&, const HitTestLocation& pointInContainer, const LayoutRect& = LayoutRect());
-    bool addNodeToRectBasedTestResult(Node*, const HitTestRequest&, const HitTestLocation& pointInContainer, const FloatRect&);
-    void append(const HitTestResult&);
+    HitTestProgress addNodeToListBasedTestResult(Node*, const HitTestRequest&, const HitTestLocation& pointInContainer, const LayoutRect& = LayoutRect());
+    HitTestProgress addNodeToListBasedTestResult(Node*, const HitTestRequest&, const HitTestLocation& pointInContainer, const FloatRect&);
+    void append(const HitTestResult&, const HitTestRequest&);
 
-    // If m_rectBasedTestResult is 0 then set it to a new NodeSet. Return *m_rectBasedTestResult. Lazy allocation makes
+    // If m_listBasedTestResult is 0 then set it to a new NodeSet. Return *m_listBasedTestResult. Lazy allocation makes
     // sense because the NodeSet is seldom necessary, and it's somewhat expensive to allocate and initialize. This method does
-    // the same thing as mutableRectBasedTestResult(), but here the return value is const.
-    WEBCORE_EXPORT const NodeSet& rectBasedTestResult() const;
+    // the same thing as mutableListBasedTestResult(), but here the return value is const.
+    WEBCORE_EXPORT const NodeSet& listBasedTestResult() const;
 
     Vector<String> dictationAlternatives() const;
 
@@ -148,7 +148,7 @@
     WEBCORE_EXPORT Element* targetElement() const;
 
 private:
-    NodeSet& mutableRectBasedTestResult(); // See above.
+    NodeSet& mutableListBasedTestResult(); // See above.
 
 #if ENABLE(VIDEO)
     HTMLMediaElement* mediaElement() const;
@@ -164,7 +164,7 @@
     RefPtr<Scrollbar> m_scrollbar;
     bool m_isOverWidget; // Returns true if we are over a widget (and not in the border/padding area of a RenderWidget for example).
 
-    mutable std::unique_ptr<NodeSet> m_rectBasedTestResult;
+    mutable std::unique_ptr<NodeSet> m_listBasedTestResult;
 };
 
 WEBCORE_EXPORT String displayString(const String&, const Node*);

Modified: trunk/Source/WebCore/rendering/InlineFlowBox.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/InlineFlowBox.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/InlineFlowBox.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1150,7 +1150,7 @@
 
     if (locationInContainer.intersects(rect)) {
         renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset))); // Don't add in m_x or m_y here, we want coords in the containing block's space.
-        if (!result.addNodeToRectBasedTestResult(renderer().element(), request, locationInContainer, rect))
+        if (result.addNodeToListBasedTestResult(renderer().element(), request, locationInContainer, rect) == HitTestProgress::Stop)
             return true;
     }
 

Modified: trunk/Source/WebCore/rendering/InlineTextBox.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/InlineTextBox.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/InlineTextBox.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -359,7 +359,7 @@
 
     if (locationInContainer.intersects(rect)) {
         renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset)));
-        if (!result.addNodeToRectBasedTestResult(renderer().textNode(), request, locationInContainer, rect))
+        if (result.addNodeToListBasedTestResult(renderer().textNode(), request, locationInContainer, rect) == HitTestProgress::Stop)
             return true;
     }
     return false;

Modified: trunk/Source/WebCore/rendering/RenderBlock.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderBlock.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderBlock.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -2494,7 +2494,7 @@
     if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) {
         updateHitTestResult(result, locationInContainer.point() - localOffset);
         // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet.
-        if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer))
+        if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer) == HitTestProgress::Stop)
            return true;
     }
 
@@ -2574,7 +2574,7 @@
         LayoutRect boundsRect(adjustedLocation, size());
         if (visibleToHitTesting() && locationInContainer.intersects(boundsRect)) {
             updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset));
-            if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect))
+            if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
                 return true;
         }
     }

Modified: trunk/Source/WebCore/rendering/RenderBox.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderBox.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderBox.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1244,7 +1244,7 @@
     boundsRect.moveBy(adjustedLocation);
     if (visibleToHitTesting() && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
         updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
-        if (!result.addNodeToRectBasedTestResult(element(), request, locationInContainer, boundsRect))
+        if (result.addNodeToListBasedTestResult(element(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
             return true;
     }
 

Modified: trunk/Source/WebCore/rendering/RenderImage.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderImage.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderImage.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -667,8 +667,8 @@
         }
     }
 
-    if (!inside && result.isRectBasedTest())
-        result.append(tempResult);
+    if (!inside && request.resultIsElementList())
+        result.append(tempResult, request);
     if (inside)
         result = tempResult;
     return inside;

Modified: trunk/Source/WebCore/rendering/RenderInline.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderInline.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderInline.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -959,9 +959,9 @@
 
     if (context.intersected()) {
         updateHitTestResult(result, tmpLocation.point());
-        // We can not use addNodeToRectBasedTestResult to determine if we fully enclose the hit-test area
+        // We cannot use addNodeToListBasedTestResult to determine if we fully enclose the hit-test area
         // because it can only handle rectangular targets.
-        result.addNodeToRectBasedTestResult(element(), request, locationInContainer);
+        result.addNodeToListBasedTestResult(element(), request, locationInContainer);
         return regionResult.contains(tmpLocation.boundingBox());
     }
     return false;

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -5097,14 +5097,15 @@
         RenderLayer* hitLayer = fixedLayer->hitTestLayer(fixedLayer->renderer().flowThreadContainingBlock()->layer(), nullptr, request, tempResult,
             hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants);
 
-        // If it a rect-based test, we can safely append the temporary result since it might had hit
+        // If it a list-based test, we can safely append the temporary result since it might had hit
         // nodes but not necesserily had hitLayer set.
-        if (result.isRectBasedTest())
-            result.append(tempResult);
+        ASSERT(!result.isRectBasedTest() || request.resultIsElementList());
+        if (request.resultIsElementList())
+            result.append(tempResult, request);
 
         if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
             resultLayer = hitLayer;
-            if (!result.isRectBasedTest())
+            if (!request.resultIsElementList())
                 result = tempResult;
             if (!depthSortDescendants)
                 break;
@@ -5271,8 +5272,8 @@
         bool insideFragmentForegroundRect = false;
         if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestDescendants, insideFragmentForegroundRect)
             && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
-            if (result.isRectBasedTest())
-                result.append(tempResult);
+            if (request.resultIsElementList())
+                result.append(tempResult, request);
             else
                 result = tempResult;
             if (!depthSortDescendants)
@@ -5279,8 +5280,8 @@
                 return this;
             // Foreground can depth-sort with descendant layers, so keep this as a candidate.
             candidateLayer = this;
-        } else if (insideFragmentForegroundRect && result.isRectBasedTest())
-            result.append(tempResult);
+        } else if (insideFragmentForegroundRect && request.resultIsElementList())
+            result.append(tempResult, request);
     }
 
     // Now check our negative z-index children.
@@ -5301,14 +5302,14 @@
         bool insideFragmentBackgroundRect = false;
         if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestSelf, insideFragmentBackgroundRect)
             && isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
-            if (result.isRectBasedTest())
-                result.append(tempResult);
+            if (request.resultIsElementList())
+                result.append(tempResult, request);
             else
                 result = tempResult;
             return this;
         }
-        if (insideFragmentBackgroundRect && result.isRectBasedTest())
-            result.append(tempResult);
+        if (insideFragmentBackgroundRect && request.resultIsElementList())
+            result.append(tempResult, request);
     }
 
     return nullptr;
@@ -5423,7 +5424,7 @@
     if (!renderer().hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) {
         // It's wrong to set innerNode, but then claim that you didn't hit anything, unless it is
         // a rect-based test.
-        ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBasedTestResult().size()));
+        ASSERT(!result.innerNode() || (request.resultIsElementList() && result.listBasedTestResult().size()));
         return false;
     }
 
@@ -5472,14 +5473,15 @@
         HitTestResult tempResult(result.hitTestLocation());
         hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants);
 
-        // If it a rect-based test, we can safely append the temporary result since it might had hit
+        // If it is a list-based test, we can safely append the temporary result since it might had hit
         // nodes but not necesserily had hitLayer set.
-        if (result.isRectBasedTest())
-            result.append(tempResult);
+        ASSERT(!result.isRectBasedTest() || request.resultIsElementList());
+        if (request.resultIsElementList())
+            result.append(tempResult, request);
 
         if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
             resultLayer = hitLayer;
-            if (!result.isRectBasedTest())
+            if (!request.resultIsElementList())
                 result = tempResult;
             if (!depthSortDescendants)
                 break;
@@ -7220,11 +7222,11 @@
 
         HitTestResult tempResult(result.hitTestLocation());
         RenderLayer* hitLayer = flowThread->layer()->hitTestLayer(flowThread->layer(), nullptr, newRequest, tempResult, hitTestRectInFlowThread, newHitTestLocation, false, transformState, zOffsetForDescendants);
-        if (result.isRectBasedTest())
-            result.append(tempResult);
+        if (request.resultIsElementList())
+            result.append(tempResult, request);
         if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
             resultLayer = hitLayer;
-            if (!result.isRectBasedTest())
+            if (!request.resultIsElementList())
                 result = tempResult;
             if (!depthSortDescendants)
                 break;

Modified: trunk/Source/WebCore/rendering/RenderTable.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderTable.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderTable.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1575,7 +1575,7 @@
     LayoutRect boundsRect(adjustedLocation, size());
     if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) {
         updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation)));
-        if (!result.addNodeToRectBasedTestResult(element(), request, locationInContainer, boundsRect))
+        if (result.addNodeToListBasedTestResult(element(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
             return true;
     }
 

Modified: trunk/Source/WebCore/rendering/RenderTableSection.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderTableSection.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderTableSection.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1548,10 +1548,10 @@
                     return true;
                 }
             }
-            if (!result.isRectBasedTest())
+            if (!request.resultIsElementList())
                 break;
         }
-        if (!result.isRectBasedTest())
+        if (!request.resultIsElementList())
             break;
     }
 

Modified: trunk/Source/WebCore/rendering/RenderWidget.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/RenderWidget.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/RenderWidget.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -372,8 +372,8 @@
 
         bool isInsideChildFrame = childRoot.hitTest(newHitTestRequest, newHitTestLocation, childFrameResult);
 
-        if (newHitTestLocation.isRectBasedTest())
-            result.append(childFrameResult);
+        if (request.resultIsElementList())
+            result.append(childFrameResult, request);
         else if (isInsideChildFrame)
             result = childFrameResult;
 

Modified: trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/SimpleLineLayoutFunctions.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -151,7 +151,7 @@
         if (!locationInContainer.intersects(lineRect))
             continue;
         renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
-        if (!result.addNodeToRectBasedTestResult(renderer.node(), request, locationInContainer, lineRect))
+        if (result.addNodeToListBasedTestResult(renderer.node(), request, locationInContainer, lineRect) == HitTestProgress::Stop)
             return true;
     }
     return false;

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGContainer.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/svg/RenderSVGContainer.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGContainer.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -26,6 +26,7 @@
 
 #include "GraphicsContext.h"
 #include "HitTestRequest.h"
+#include "HitTestResult.h"
 #include "LayoutRepainter.h"
 #include "RenderIterator.h"
 #include "RenderSVGResourceFilter.h"
@@ -181,7 +182,8 @@
     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
         if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
             updateHitTestResult(result, LayoutPoint(localPoint));
-            return true;
+            if (result.addNodeToListBasedTestResult(child->node(), request, localPoint) == HitTestProgress::Stop)
+                return true;
         }
     }
 
@@ -188,7 +190,8 @@
     // Accessibility wants to return SVG containers, if appropriate.
     if (request.type() & HitTestRequest::AccessibilityHitTest && m_objectBoundingBox.contains(localPoint)) {
         updateHitTestResult(result, LayoutPoint(localPoint));
-        return true;
+        if (result.addNodeToListBasedTestResult(&element(), request, localPoint) == HitTestProgress::Stop)
+            return true;
     }
     
     // Spec: Only graphical elements can be targeted by the mouse, period.

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGImage.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/svg/RenderSVGImage.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGImage.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -28,6 +28,7 @@
 
 #include "FloatQuad.h"
 #include "GraphicsContext.h"
+#include "HitTestResult.h"
 #include "LayoutRepainter.h"
 #include "PointerEventsHitRules.h"
 #include "RenderImageResource.h"
@@ -198,7 +199,8 @@
         if (hitRules.canHitFill) {
             if (m_objectBoundingBox.contains(localPoint)) {
                 updateHitTestResult(result, LayoutPoint(localPoint));
-                return true;
+                if (result.addNodeToListBasedTestResult(&imageElement(), request, localPoint) == HitTestProgress::Stop)
+                    return true;
             }
         }
     }

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGRoot.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/svg/RenderSVGRoot.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGRoot.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -420,7 +420,7 @@
             // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet.
             if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
                 updateHitTestResult(result, pointInBorderBox);
-                if (!result.addNodeToRectBasedTestResult(child->node(), request, locationInContainer))
+                if (result.addNodeToListBasedTestResult(child->node(), request, locationInContainer) == HitTestProgress::Stop)
                     return true;
             }
         }
@@ -435,7 +435,7 @@
         LayoutRect boundsRect(accumulatedOffset + location(), size());
         if (locationInContainer.intersects(boundsRect)) {
             updateHitTestResult(result, pointInBorderBox);
-            if (!result.addNodeToRectBasedTestResult(&svgSVGElement(), request, locationInContainer, boundsRect))
+            if (result.addNodeToListBasedTestResult(&svgSVGElement(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
                 return true;
         }
     }

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGShape.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/svg/RenderSVGShape.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGShape.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -32,6 +32,7 @@
 #include "FloatQuad.h"
 #include "GraphicsContext.h"
 #include "HitTestRequest.h"
+#include "HitTestResult.h"
 #include "LayoutRepainter.h"
 #include "PointerEventsHitRules.h"
 #include "RenderSVGResourceMarker.h"
@@ -353,7 +354,8 @@
         if ((hitRules.canHitStroke && (svgStyle.hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
             || (hitRules.canHitFill && (svgStyle.hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
             updateHitTestResult(result, LayoutPoint(localPoint));
-            return true;
+            if (result.addNodeToListBasedTestResult(&graphicsElement(), request, localPoint) == HitTestProgress::Stop)
+                return true;
         }
     }
     return false;

Modified: trunk/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp (219960 => 219961)


--- trunk/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -671,7 +671,7 @@
                     
                     if (fragmentQuad.containsPoint(locationInContainer.point())) {
                         renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
-                        if (!result.addNodeToRectBasedTestResult(&renderer().textNode(), request, locationInContainer, rect))
+                        if (result.addNodeToListBasedTestResult(&renderer().textNode(), request, locationInContainer, rect) == HitTestProgress::Stop)
                             return true;
                     }
                 }

Modified: trunk/Source/WebCore/testing/Internals.cpp (219960 => 219961)


--- trunk/Source/WebCore/testing/Internals.cpp	2017-07-26 20:11:38 UTC (rev 219960)
+++ trunk/Source/WebCore/testing/Internals.cpp	2017-07-26 20:51:26 UTC (rev 219961)
@@ -1829,7 +1829,7 @@
     float zoomFactor = frame->pageZoomFactor();
     LayoutPoint point(centerX * zoomFactor + frameView->scrollX(), centerY * zoomFactor + frameView->scrollY());
 
-    HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active;
+    HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::CollectMultipleElements;
     if (ignoreClipping)
         hitType |= HitTestRequest::IgnoreClipping;
     if (!allowUserAgentShadowContent)
@@ -1843,25 +1843,14 @@
     if (!request.ignoreClipping() && !frameView->visibleContentRect().intersects(HitTestLocation::rectForPoint(point, topPadding, rightPadding, bottomPadding, leftPadding)))
         return nullptr;
 
+    HitTestResult result(point, topPadding, rightPadding, bottomPadding, leftPadding);
+    renderView->hitTest(request, result);
+    const HitTestResult::NodeSet& nodeSet = result.listBasedTestResult();
     Vector<Ref<Node>> matches;
+    matches.reserveInitialCapacity(nodeSet.size());
+    for (auto& node : nodeSet)
+        matches.uncheckedAppend(*node);
 
-    // Need padding to trigger a rect based hit test, but we want to return a NodeList
-    // so we special case this.
-    if (!topPadding && !rightPadding && !bottomPadding && !leftPadding) {
-        HitTestResult result(point);
-        renderView->hitTest(request, result);
-        if (result.innerNode())
-            matches.append(*result.innerNode()->deprecatedShadowAncestorNode());
-    } else {
-        HitTestResult result(point, topPadding, rightPadding, bottomPadding, leftPadding);
-        renderView->hitTest(request, result);
-
-        const HitTestResult::NodeSet& nodeSet = result.rectBasedTestResult();
-        matches.reserveInitialCapacity(nodeSet.size());
-        for (auto& node : nodeSet)
-            matches.uncheckedAppend(*node);
-    }
-
     return RefPtr<NodeList> { StaticNodeList::create(WTFMove(matches)) };
 }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to