diff options
Diffstat (limited to 'accessible/tests/browser/hittest')
-rw-r--r-- | accessible/tests/browser/hittest/browser.ini | 18 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_browser.js | 68 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_general.js | 225 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_shadowroot.js | 61 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_text.js | 53 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_zoom.js | 38 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/browser_test_zoom_text.js | 64 | ||||
-rw-r--r-- | accessible/tests/browser/hittest/head.js | 113 |
8 files changed, 640 insertions, 0 deletions
diff --git a/accessible/tests/browser/hittest/browser.ini b/accessible/tests/browser/hittest/browser.ini new file mode 100644 index 0000000000..93c437c7c2 --- /dev/null +++ b/accessible/tests/browser/hittest/browser.ini @@ -0,0 +1,18 @@ +[DEFAULT] +subsuite = a11y +support-files = + head.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/browser/*.jsm + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + +[browser_test_browser.js] +[browser_test_general.js] +[browser_test_shadowroot.js] +[browser_test_text.js] +[browser_test_zoom_text.js] +[browser_test_zoom.js] +skip-if = + os == 'linux' && bits == 64 # Bug 1778220 + os == 'win' # Bug 1778220 diff --git a/accessible/tests/browser/hittest/browser_test_browser.js b/accessible/tests/browser/hittest/browser_test_browser.js new file mode 100644 index 0000000000..477af42fe9 --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_browser.js @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +async function runTests(browser, accDoc) { + // Hit testing. See bug #726097 + await invokeContentTask(browser, [], () => + content.document.getElementById("hittest").scrollIntoView(true) + ); + + const dpr = await getContentDPR(browser); + const hititem = findAccessibleChildByID(accDoc, "hititem"); + const hittest = findAccessibleChildByID(accDoc, "hittest"); + const outerDocAcc = accDoc.parent; + const rootAcc = CommonUtils.getRootAccessible(document); + + const [hitX, hitY, hitWidth, hitHeight] = Layout.getBounds(hititem, dpr); + // "hititem" node has the full screen width, so when we divide it by 2, we are + // still way outside the inline content. + const tgtX = hitX + hitWidth / 2; + const tgtY = hitY + hitHeight / 2; + + let hitAcc = rootAcc.getDeepestChildAtPoint(tgtX, tgtY); + is( + hitAcc, + hititem, + `Hit match at ${tgtX},${tgtY} (root doc deepest child). Found: ${prettyName( + hitAcc + )}` + ); + + const hitAcc2 = accDoc.getDeepestChildAtPoint(tgtX, tgtY); + is( + hitAcc, + hitAcc2, + `Hit match at ${tgtX},${tgtY} (doc deepest child). Found: ${prettyName( + hitAcc2 + )}` + ); + + hitAcc = outerDocAcc.getChildAtPoint(tgtX, tgtY); + is( + hitAcc, + accDoc, + `Hit match at ${tgtX},${tgtY} (outer doc child). Found: ${prettyName( + hitAcc + )}` + ); + + hitAcc = accDoc.getChildAtPoint(tgtX, tgtY); + is( + hitAcc, + hittest, + `Hit match at ${tgtX},${tgtY} (doc child). Found: ${prettyName(hitAcc)}` + ); +} + +addAccessibleTask( + ` + <div id="hittest"> + <div id="hititem"><span role="img">img</span>item</div> + </div> + `, + runTests, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/hittest/browser_test_general.js b/accessible/tests/browser/hittest/browser_test_general.js new file mode 100644 index 0000000000..c2d5b3906a --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_general.js @@ -0,0 +1,225 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +async function runTests(browser, accDoc) { + await waitForImageMap(browser, accDoc); + const dpr = await getContentDPR(browser); + + await testChildAtPoint( + dpr, + 3, + 3, + findAccessibleChildByID(accDoc, "list"), + findAccessibleChildByID(accDoc, "listitem"), + findAccessibleChildByID(accDoc, "inner").firstChild + ); + todo( + false, + "Bug 746974 - children must match on all platforms. On Windows, " + + "ChildAtPoint with eDeepestChild is incorrectly ignoring MustPrune " + + "for the graphic." + ); + + const txt = findAccessibleChildByID(accDoc, "txt"); + await testChildAtPoint(dpr, 1, 1, txt, txt, txt); + + info( + "::MustPrune case, point is outside of textbox accessible but is in document." + ); + await testChildAtPoint(dpr, -1, -1, txt, null, null); + + info("::MustPrune case, point is outside of root accessible."); + await testChildAtPoint(dpr, -10000, -10000, txt, null, null); + + info("Not specific case, point is inside of btn accessible."); + const btn = findAccessibleChildByID(accDoc, "btn"); + await testChildAtPoint(dpr, 1, 1, btn, btn, btn); + + info("Not specific case, point is outside of btn accessible."); + await testChildAtPoint(dpr, -1, -1, btn, null, null); + + info( + "Out of flow accessible testing, do not return out of flow accessible " + + "because it's not a child of the accessible even though visually it is." + ); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs" + ); + const doc = content.document; + const rectArea = CommonUtils.getNode("area", doc).getBoundingClientRect(); + const outOfFlow = CommonUtils.getNode("outofflow", doc); + outOfFlow.style.left = rectArea.left + "px"; + outOfFlow.style.top = rectArea.top + "px"; + }); + + const area = findAccessibleChildByID(accDoc, "area"); + await testChildAtPoint(dpr, 1, 1, area, area, area); + + info("Test image maps. Their children are not in the layout tree."); + const imgmap = findAccessibleChildByID(accDoc, "imgmap"); + const theLetterA = imgmap.firstChild; + await hitTest(browser, imgmap, theLetterA, theLetterA); + await hitTest( + browser, + findAccessibleChildByID(accDoc, "container"), + imgmap, + theLetterA + ); + + info("hit testing for element contained by zero-width element"); + const container2Input = findAccessibleChildByID(accDoc, "container2_input"); + await hitTest( + browser, + findAccessibleChildByID(accDoc, "container2"), + container2Input, + container2Input + ); + + info("hittesting table, row, cells -- rows are not in the layout tree"); + const table = findAccessibleChildByID(accDoc, "table"); + const row = findAccessibleChildByID(accDoc, "row"); + const cell1 = findAccessibleChildByID(accDoc, "cell1"); + + await hitTest(browser, table, row, cell1); + + info("Testing that an inaccessible child doesn't break hit testing"); + const containerWithInaccessibleChild = findAccessibleChildByID( + accDoc, + "containerWithInaccessibleChild" + ); + const containerWithInaccessibleChildP2 = findAccessibleChildByID( + accDoc, + "containerWithInaccessibleChild_p2" + ); + await hitTest( + browser, + containerWithInaccessibleChild, + containerWithInaccessibleChildP2, + containerWithInaccessibleChildP2.firstChild + ); +} + +addAccessibleTask( + ` + <div role="list" id="list"> + <div role="listitem" id="listitem"><span title="foo" id="inner">inner</span>item</div> + </div> + + <span role="button">button1</span><span role="button" id="btn">button2</span> + + <span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span> + + <div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;"> + </div> + <div id="area" style="width: 100px; height: 100px; background-color: blue;"></div> + + <map name="atoz_map"> + <area id="thelettera" href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a" + coords="0,0,15,15" alt="thelettera" shape="rect"/> + </map> + + <div id="container"> + <img id="imgmap" width="447" height="15" usemap="#atoz_map" src="http://example.com/a11y/accessible/tests/mochitest/letters.gif"/> + </div> + + <div id="container2" style="width: 0px"> + <input id="container2_input"> + </div> + + <table id="table" border> + <tr id="row"> + <td id="cell1">hello</td> + <td id="cell2">world</td> + </tr> + </table> + + <div id="containerWithInaccessibleChild"> + <p>hi</p> + <p aria-hidden="true">hi</p> + <p id="containerWithInaccessibleChild_p2">bye</p> + </div> + `, + runTests, + { + iframe: true, + remoteIframe: true, + // Ensure that all hittest elements are in view. + iframeAttrs: { style: "width: 600px; height: 600px; padding: 10px;" }, + } +); + +addAccessibleTask( + ` + <div id="container"> + <h1 id="a">A</h1><h1 id="b">B</h1> + </div> + `, + async function(browser, accDoc) { + const a = findAccessibleChildByID(accDoc, "a"); + const b = findAccessibleChildByID(accDoc, "b"); + const dpr = await getContentDPR(browser); + // eslint-disable-next-line no-unused-vars + const [x, y, w, h] = Layout.getBounds(a, dpr); + // The point passed below will be made relative to `b`, but + // we'd like to test a point within `a`. Pass `a`s negative + // width for an x offset. Pass zero as a y offset, + // assuming the headings are on the same line. + await testChildAtPoint(dpr, -w, 0, b, null, null); + }, + { + iframe: true, + remoteIframe: true, + // Ensure that all hittest elements are in view. + iframeAttrs: { style: "width: 600px; height: 600px; padding: 10px;" }, + } +); + +addAccessibleTask( + ` + <style> + div { + width: 50px; + height: 50px; + position: relative; + } + + div > div { + width: 30px; + height: 30px; + position: absolute; + opacity: 0.9; + } + </style> + <div id="a" style="background-color: orange;"> + <div id="aa" style="background-color: purple;"></div> + </div> + <div id="b" style="background-color: yellowgreen;"> + <div id="bb" style="top: -30px; background-color: turquoise"></div> + </div>`, + async function(browser, accDoc) { + const a = findAccessibleChildByID(accDoc, "a"); + const aa = findAccessibleChildByID(accDoc, "aa"); + const dpr = await getContentDPR(browser); + const [, , w, h] = Layout.getBounds(a, dpr); + // test upper left of `a` + await testChildAtPoint(dpr, 1, 1, a, aa, aa); + // test upper right of `a` + await testChildAtPoint(dpr, w - 1, 1, a, a, a); + // test just outside upper left of `a` + await testChildAtPoint(dpr, 1, -1, a, null, null); + // test halfway down/left of `a` + await testChildAtPoint(dpr, 1, Math.round(h / 2), a, a, a); + }, + { + chrome: true, + topLevel: true, + iframe: false, + remoteIframe: false, + // Ensure that all hittest elements are in view. + iframeAttrs: { style: "width: 600px; height: 600px; padding: 10px;" }, + } +); diff --git a/accessible/tests/browser/hittest/browser_test_shadowroot.js b/accessible/tests/browser/hittest/browser_test_shadowroot.js new file mode 100644 index 0000000000..94a5ce071a --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_shadowroot.js @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +async function runTests(browser, accDoc) { + const dpr = await getContentDPR(browser); + let componentAcc = findAccessibleChildByID(accDoc, "component1"); + await testChildAtPoint( + dpr, + 1, + 1, + componentAcc, + componentAcc.firstChild, + componentAcc.firstChild + ); + + componentAcc = findAccessibleChildByID(accDoc, "component2"); + await testChildAtPoint( + dpr, + 1, + 1, + componentAcc, + componentAcc.firstChild, + componentAcc.firstChild + ); +} + +addAccessibleTask( + ` + <div role="group" class="components" id="component1" style="display: inline-block;"> + <!-- + <div role="button" id="component-child" + style="width: 100px; height: 100px; background-color: pink;"> + </div> + --> + </div> + <div role="group" class="components" id="component2" style="display: inline-block;"> + <!-- + <button>Hello world</button> + --> + </div> + <script> + // This routine adds the comment children of each 'component' to its + // shadow root. + var components = document.querySelectorAll(".components"); + for (var i = 0; i < components.length; i++) { + var component = components[i]; + var shadow = component.attachShadow({mode: "open"}); + for (var child = component.firstChild; child; child = child.nextSibling) { + if (child.nodeType === 8) + // eslint-disable-next-line no-unsanitized/property + shadow.innerHTML = child.data; + } + } + </script> + `, + runTests, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/hittest/browser_test_text.js b/accessible/tests/browser/hittest/browser_test_text.js new file mode 100644 index 0000000000..1bd314e438 --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_text.js @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +addAccessibleTask( + ` +a +<div id="noChars" style="width: 5px; height: 5px;"><p></p></div> +<p id="twoText"><span>a</span><span>b</span></p> +<div id="iframeAtEnd" style="width: 20px; height: 20px;"> + a + <iframe width="1" height="1"></iframe> +</div> + `, + async function(browser, docAcc) { + const dpr = await getContentDPR(browser); + // Test getOffsetAtPoint on a container containing no characters. The inner + // container does not include the requested point, but the outer one does. + const noChars = findAccessibleChildByID(docAcc, "noChars", [ + Ci.nsIAccessibleText, + ]); + let [x, y] = Layout.getBounds(noChars, dpr); + await testOffsetAtPoint(noChars, x, y, COORDTYPE_SCREEN_RELATIVE, -1); + + // Test that the correct offset is returned for a point in a second text + // leaf. + const twoText = findAccessibleChildByID(docAcc, "twoText", [ + Ci.nsIAccessibleText, + ]); + const text2 = twoText.getChildAt(1); + [x, y] = Layout.getBounds(text2, dpr); + await testOffsetAtPoint(twoText, x, y, COORDTYPE_SCREEN_RELATIVE, 1); + + // Test offsetAtPoint when there is an iframe at the end of the container. + const iframeAtEnd = findAccessibleChildByID(docAcc, "iframeAtEnd", [ + Ci.nsIAccessibleText, + ]); + let width; + let height; + [x, y, width, height] = Layout.getBounds(iframeAtEnd, dpr); + x += width - 1; + y += height - 1; + await testOffsetAtPoint(iframeAtEnd, x, y, COORDTYPE_SCREEN_RELATIVE, -1); + }, + { + topLevel: true, + iframe: true, + remoteIframe: true, + chrome: true, + } +); diff --git a/accessible/tests/browser/hittest/browser_test_zoom.js b/accessible/tests/browser/hittest/browser_test_zoom.js new file mode 100644 index 0000000000..84383df483 --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_zoom.js @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +async function runTests(browser, accDoc) { + if (Services.appinfo.OS !== "Darwin") { + const p1 = findAccessibleChildByID(accDoc, "p1"); + const p2 = findAccessibleChildByID(accDoc, "p2"); + await hitTest(browser, accDoc, p1, p1.firstChild); + await hitTest(browser, accDoc, p2, p2.firstChild); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs" + ); + + Layout.zoomDocument(content.document, 2.0); + content.document.body.offsetTop; // getBounds doesn't flush layout on its own. + }); + + await hitTest(browser, accDoc, p1, p1.firstChild); + await hitTest(browser, accDoc, p2, p2.firstChild); + } else { + todo( + false, + "Bug 746974 - deepest child must be correct on all platforms, disabling on Mac!" + ); + } +} + +addAccessibleTask(`<p id="p1">para 1</p><p id="p2">para 2</p>`, runTests, { + iframe: true, + remoteIframe: true, + // Ensure that all hittest elements are in view. + iframeAttrs: { style: "left: 100px; top: 100px;" }, +}); diff --git a/accessible/tests/browser/hittest/browser_test_zoom_text.js b/accessible/tests/browser/hittest/browser_test_zoom_text.js new file mode 100644 index 0000000000..9e429c16b3 --- /dev/null +++ b/accessible/tests/browser/hittest/browser_test_zoom_text.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +async function runTests(browser, accDoc) { + const expectedLength = await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs" + ); + const hyperText = CommonUtils.getNode("paragraph", content.document); + return Math.floor(hyperText.textContent.length / 2); + }); + const hyperText = findAccessibleChildByID(accDoc, "paragraph", [ + Ci.nsIAccessibleText, + ]); + const textNode = hyperText.firstChild; + + let [x, y, width, height] = Layout.getBounds( + textNode, + await getContentDPR(browser) + ); + + await testOffsetAtPoint( + hyperText, + x + width / 2, + y + height / 2, + COORDTYPE_SCREEN_RELATIVE, + expectedLength + ); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs" + ); + + Layout.zoomDocument(content.document, 2.0); + content.document.body.offsetTop; // getBounds doesn't flush layout on its own. + }); + + [x, y, width, height] = Layout.getBounds( + textNode, + await getContentDPR(browser) + ); + + await testOffsetAtPoint( + hyperText, + x + width / 2, + y + height / 2, + COORDTYPE_SCREEN_RELATIVE, + expectedLength + ); +} + +addAccessibleTask( + `<p id="paragraph" style="font-family: monospace;">hello world hello world</p>`, + runTests, + { + iframe: true, + remoteIframe: true, + iframeAttrs: { style: "width: 600px; height: 600px;" }, + } +); diff --git a/accessible/tests/browser/hittest/head.js b/accessible/tests/browser/hittest/head.js new file mode 100644 index 0000000000..867f8cde35 --- /dev/null +++ b/accessible/tests/browser/hittest/head.js @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ + +/* exported CommonUtils, testChildAtPoint, Layout, hitTest, testOffsetAtPoint */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); + +const { CommonUtils } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.sys.mjs" +); + +const { Layout } = ChromeUtils.importESModule( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.sys.mjs" +); + +function getChildAtPoint(container, x, y, findDeepestChild) { + try { + return findDeepestChild + ? container.getDeepestChildAtPoint(x, y) + : container.getChildAtPoint(x, y); + } catch (e) { + // Failed to get child at point. + } + info("could not get child at point"); + return null; +} + +async function testChildAtPoint(dpr, x, y, container, child, grandChild) { + const [containerX, containerY] = Layout.getBounds(container, dpr); + x += containerX; + y += containerY; + let actual = null; + await untilCacheIs( + () => { + actual = getChildAtPoint(container, x, y, false); + return actual; + }, + child, + `Wrong direct child accessible at the point (${x}, ${y}) of ${CommonUtils.prettyName( + container + )}, sought ${CommonUtils.prettyName( + child + )} and got ${CommonUtils.prettyName(actual)}` + ); + actual = null; + await untilCacheIs( + () => { + actual = getChildAtPoint(container, x, y, true); + return actual; + }, + grandChild, + `Wrong deepest child accessible at the point (${x}, ${y}) of ${CommonUtils.prettyName( + container + )}, sought ${CommonUtils.prettyName( + grandChild + )} and got ${CommonUtils.prettyName(actual)}` + ); +} + +/** + * Test if getChildAtPoint returns the given child and grand child accessibles + * at coordinates of child accessible (direct and deep hit test). + */ +async function hitTest(browser, container, child, grandChild) { + const [childX, childY] = await getContentBoundsForDOMElm( + browser, + getAccessibleDOMNodeID(child) + ); + const x = childX + 1; + const y = childY + 1; + + await untilCacheIs( + () => getChildAtPoint(container, x, y, false), + child, + `Wrong direct child accessible at the point (${x}, ${y}) of ${CommonUtils.prettyName( + container + )}, sought ${CommonUtils.prettyName(child)}` + ); + await untilCacheIs( + () => getChildAtPoint(container, x, y, true), + grandChild, + `Wrong deepest child accessible at the point (${x}, ${y}) of ${CommonUtils.prettyName( + container + )}, sought ${CommonUtils.prettyName(grandChild)}` + ); +} + +/** + * Test if getOffsetAtPoint returns the given text offset at given coordinates. + */ +async function testOffsetAtPoint(hyperText, x, y, coordType, expectedOffset) { + await untilCacheIs( + () => hyperText.getOffsetAtPoint(x, y, coordType), + expectedOffset, + `Wrong offset at given point (${x}, ${y}) for ${prettyName(hyperText)}` + ); +} |