+2017-07-26 Ali Juma <ajuma@chromium.org>
+
+ 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 <bburg@apple.com>
Remove WEB_TIMING feature flag
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
+2017-07-26 Ali Juma <ajuma@chromium.org>
+
+ 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 <fwang@igalia.com>
Add attribute allow-top-navigation-by-user-activation to iframe sandbox
--- /dev/null
+
+
+
+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
+
--- /dev/null
+
+
+PASS elementsFromPoint on the root document for points in iframe elements
+PASS elementsFromPoint on inner documents
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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="resources/iframe1.html"></iframe>
+<iframe id="scrollableIframe" src="resources/iframe2.html"></iframe>
--- /dev/null
+
+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
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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>
--- /dev/null
+
+
+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
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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>
--- /dev/null
+
+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
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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>
--- /dev/null
+
+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>
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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='data:image/svg+xml;utf8,<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="rgba(180,0,0,0.2)"/></svg>'/>
+ <image id='bottomLeftImage2' x='10' y='210' width='80' height='80' xlink:href='data:image/svg+xml;utf8,<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="rgba(180,0,0,0.2)"/></svg>'/>
+ </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>
--- /dev/null
+
+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
+
--- /dev/null
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<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>
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
--- /dev/null
+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);
+}
--- /dev/null
+<!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>
--- /dev/null
+<!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>
+2017-07-26 Ali Juma <ajuma@chromium.org>
+
+ 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 (pdr@chromium.org):
+ -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 <cturner@igalia.com>
[GStreamer] Review WebKitWebSource after r219252.
// 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.
#include "NodeRareData.h"
#include "Page.h"
#include "PointerLockController.h"
+#include "PseudoElement.h"
#include "RenderView.h"
#include "RuntimeEnabledFeatures.h"
#include "Settings.h"
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)
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;
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())
#include "DocumentOrderedMap.h"
#include <memory>
#include <wtf/Forward.h>
+#include <wtf/Vector.h>
#include <wtf/text/AtomicString.h>
namespace WebCore {
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
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,
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;
}
#pragma once
+#include <wtf/Assertions.h>
+
namespace WebCore {
class HitTestRequest {
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;
HitTestRequest(HitTestRequestType requestType = ReadOnly | Active | DisallowUserAgentShadowContent)
: m_requestType(requestType)
{
+ ASSERT(!(requestType & IncludeAllElementsUnderPoint) || (requestType & CollectMultipleElements));
}
bool readOnly() const { return m_requestType & ReadOnly; }
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(); }
, 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()
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;
}
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();
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
class Scrollbar;
class URL;
+enum class HitTestProgress { Stop, Continue };
+
class HitTestResult {
public:
typedef ListHashSet<RefPtr<Node>> NodeSet;
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;
WEBCORE_EXPORT Element* targetElement() const;
private:
- NodeSet& mutableRectBasedTestResult(); // See above.
+ NodeSet& mutableListBasedTestResult(); // See above.
#if ENABLE(VIDEO)
HTMLMediaElement* mediaElement() const;
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*);
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;
}
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;
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;
}
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;
}
}
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;
}
}
}
- if (!inside && result.isRectBasedTest())
- result.append(tempResult);
+ if (!inside && request.resultIsElementList())
+ result.append(tempResult, request);
if (inside)
result = tempResult;
return inside;
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;
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;
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)
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.
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;
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;
}
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;
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;
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;
}
return true;
}
}
- if (!result.isRectBasedTest())
+ if (!request.resultIsElementList())
break;
}
- if (!result.isRectBasedTest())
+ if (!request.resultIsElementList())
break;
}
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;
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;
#include "GraphicsContext.h"
#include "HitTestRequest.h"
+#include "HitTestResult.h"
#include "LayoutRepainter.h"
#include "RenderIterator.h"
#include "RenderSVGResourceFilter.h"
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;
}
}
// 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.
#include "FloatQuad.h"
#include "GraphicsContext.h"
+#include "HitTestResult.h"
#include "LayoutRepainter.h"
#include "PointerEventsHitRules.h"
#include "RenderImageResource.h"
if (hitRules.canHitFill) {
if (m_objectBoundingBox.contains(localPoint)) {
updateHitTestResult(result, LayoutPoint(localPoint));
- return true;
+ if (result.addNodeToListBasedTestResult(&imageElement(), request, localPoint) == HitTestProgress::Stop)
+ return true;
}
}
}
// 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;
}
}
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;
}
}
#include "FloatQuad.h"
#include "GraphicsContext.h"
#include "HitTestRequest.h"
+#include "HitTestResult.h"
#include "LayoutRepainter.h"
#include "PointerEventsHitRules.h"
#include "RenderSVGResourceMarker.h"
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;
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;
}
}
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)
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;
-
- // 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);
- }
+ matches.reserveInitialCapacity(nodeSet.size());
+ for (auto& node : nodeSet)
+ matches.uncheckedAppend(*node);
return RefPtr<NodeList> { StaticNodeList::create(WTFMove(matches)) };
}