summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser/scroll
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--accessible/tests/browser/scroll/browser.toml19
-rw-r--r--accessible/tests/browser/scroll/browser_scrollToPoint.js31
-rw-r--r--accessible/tests/browser/scroll/browser_test_scrollTo.js53
-rw-r--r--accessible/tests/browser/scroll/browser_test_scroll_bounds.js662
-rw-r--r--accessible/tests/browser/scroll/browser_test_scroll_substring.js67
-rw-r--r--accessible/tests/browser/scroll/browser_test_zoom_text.js145
-rw-r--r--accessible/tests/browser/scroll/head.js18
7 files changed, 995 insertions, 0 deletions
diff --git a/accessible/tests/browser/scroll/browser.toml b/accessible/tests/browser/scroll/browser.toml
new file mode 100644
index 0000000000..ef637fe9a5
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser.toml
@@ -0,0 +1,19 @@
+[DEFAULT]
+subsuite = "a11y"
+support-files = [
+ "head.js",
+ "!/accessible/tests/browser/shared-head.js",
+ "!/accessible/tests/browser/*.jsm",
+ "!/accessible/tests/mochitest/*.js",
+]
+prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"]
+
+["browser_scrollToPoint.js"]
+
+["browser_test_scrollTo.js"]
+
+["browser_test_scroll_bounds.js"]
+
+["browser_test_scroll_substring.js"]
+
+["browser_test_zoom_text.js"]
diff --git a/accessible/tests/browser/scroll/browser_scrollToPoint.js b/accessible/tests/browser/scroll/browser_scrollToPoint.js
new file mode 100644
index 0000000000..810478fa76
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser_scrollToPoint.js
@@ -0,0 +1,31 @@
+/* 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";
+
+/* import-globals-from ../../mochitest/layout.js */
+loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR });
+
+/**
+ * Test nsIAccessible::scrollToPoint.
+ */
+addAccessibleTask(
+ `
+<hr style="height: 100vh;">
+<p id="p">hi</p>
+<hr style="height: 100vh;">
+ `,
+ async function (browser, docAcc) {
+ const [docX, docY] = getPos(docAcc);
+ const p = findAccessibleChildByID(docAcc, "p");
+ const [pX] = getPos(p);
+ info("Scrolling p");
+ let scrolled = waitForEvent(EVENT_SCROLLING_END, docAcc);
+ p.scrollToPoint(COORDTYPE_SCREEN_RELATIVE, docX, docY);
+ await scrolled;
+ // We can only scroll this vertically.
+ testPos(p, [pX, docY]);
+ },
+ { chrome: true, topLevel: true, remoteIframe: true }
+);
diff --git a/accessible/tests/browser/scroll/browser_test_scrollTo.js b/accessible/tests/browser/scroll/browser_test_scrollTo.js
new file mode 100644
index 0000000000..c007c62d82
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser_test_scrollTo.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";
+
+function getCenterY(acc) {
+ const y = {};
+ const h = {};
+ acc.getBounds({}, y, {}, h);
+ return y.value + h.value / 2;
+}
+
+/**
+ * Test nsIAccessible::scrollTo.
+ */
+addAccessibleTask(
+ `
+<div id="scroller" style="height: 100vh; overflow: scroll;">
+ <hr style="height: 100vh;">
+ <p id="p1" style="height: 10vh;">a</p>
+ <hr style="height: 100vh;">
+ <p id="p2" style="height: 10vh;">b</p>
+ <hr style="height: 100vh;">
+</div>
+ `,
+ async function (browser, docAcc) {
+ const scroller = findAccessibleChildByID(docAcc, "scroller");
+ const scrollerY = getCenterY(scroller);
+ // scroller can only show one of p1 or p2, not both.
+ const p1 = findAccessibleChildByID(docAcc, "p1");
+ info("scrollTo p1");
+ let scrolled = waitForEvent(
+ nsIAccessibleEvent.EVENT_SCROLLING_END,
+ scroller
+ );
+ p1.scrollTo(SCROLL_TYPE_ANYWHERE);
+ await scrolled;
+ isWithin(getCenterY(p1), scrollerY, 10, "p1 scrolled to center");
+ const p2 = findAccessibleChildByID(docAcc, "p2");
+ info("scrollTo p2");
+ scrolled = waitForEvent(nsIAccessibleEvent.EVENT_SCROLLING_END, scroller);
+ p2.scrollTo(SCROLL_TYPE_ANYWHERE);
+ await scrolled;
+ isWithin(getCenterY(p2), scrollerY, 10, "p2 scrolled to center");
+ info("scrollTo p1");
+ scrolled = waitForEvent(nsIAccessibleEvent.EVENT_SCROLLING_END, scroller);
+ p1.scrollTo(SCROLL_TYPE_ANYWHERE);
+ await scrolled;
+ isWithin(getCenterY(p1), scrollerY, 10, "p1 scrolled to center");
+ },
+ { topLevel: true, iframe: true, remoteIframe: true, chrome: true }
+);
diff --git a/accessible/tests/browser/scroll/browser_test_scroll_bounds.js b/accessible/tests/browser/scroll/browser_test_scroll_bounds.js
new file mode 100644
index 0000000000..31de002cda
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser_test_scroll_bounds.js
@@ -0,0 +1,662 @@
+/* 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";
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts(
+ { name: "layout.js", dir: MOCHITESTS_DIR },
+ { name: "role.js", dir: MOCHITESTS_DIR }
+);
+requestLongerTimeout(2);
+
+const appUnitsPerDevPixel = 60;
+
+function testCachedScrollPosition(
+ acc,
+ expectedX,
+ expectedY,
+ shouldBeEmpty = false
+) {
+ let cachedPosition = "";
+ try {
+ cachedPosition = acc.cache.getStringProperty("scroll-position");
+ } catch (e) {
+ info("Cache was not populated");
+ // If the key doesn't exist, this frame is not scrollable.
+ return shouldBeEmpty;
+ }
+
+ // The value we retrieve from the cache is in app units, but the values
+ // passed in are in pixels. Since the retrieved value is a string,
+ // and harder to modify, adjust our expected x and y values to match its units.
+ return (
+ cachedPosition ==
+ `${expectedX * appUnitsPerDevPixel}, ${expectedY * appUnitsPerDevPixel}`
+ );
+}
+
+function getCachedBounds(acc) {
+ let cachedBounds = "";
+ try {
+ cachedBounds = acc.cache.getStringProperty("relative-bounds");
+ } catch (e) {
+ ok(false, "Unable to fetch cached bounds from cache!");
+ }
+ return cachedBounds;
+}
+
+/**
+ * Test bounds of accessibles after scrolling
+ */
+addAccessibleTask(
+ `
+ <div id='square' style='height:100px; width:100px; background:green; margin-top:3000px; margin-bottom:4000px;'>
+ </div>
+
+ <div id='rect' style='height:40px; width:200px; background:blue; margin-bottom:3400px'>
+ </div>
+ `,
+ async function (browser, docAcc) {
+ ok(docAcc, "iframe document acc is present");
+ await testBoundsWithContent(docAcc, "square", browser);
+ await testBoundsWithContent(docAcc, "rect", browser);
+
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("square").scrollIntoView();
+ });
+
+ await waitForContentPaint(browser);
+
+ await testBoundsWithContent(docAcc, "square", browser);
+ await testBoundsWithContent(docAcc, "rect", browser);
+
+ // Scroll rect into view, but also make it reflow so we can be sure the
+ // bounds are correct for reflowed frames.
+ await invokeContentTask(browser, [], () => {
+ const rect = content.document.getElementById("rect");
+ rect.scrollIntoView();
+ rect.style.width = "300px";
+ rect.offsetTop; // Flush layout.
+ rect.style.width = "200px";
+ rect.offsetTop; // Flush layout.
+ });
+
+ await waitForContentPaint(browser);
+ await testBoundsWithContent(docAcc, "square", browser);
+ await testBoundsWithContent(docAcc, "rect", browser);
+ },
+ { iframe: true, remoteIframe: true, chrome: true }
+);
+
+/**
+ * Test scroll offset on cached accessibles
+ */
+addAccessibleTask(
+ `
+ <div id='square' style='height:100px; width:100px; background:green; margin-top:3000px; margin-bottom:4000px;'>
+ </div>
+
+ <div id='rect' style='height:40px; width:200px; background:blue; margin-bottom:3400px'>
+ </div>
+ `,
+ async function (browser, docAcc) {
+ ok(docAcc, "iframe document acc is present");
+ await untilCacheOk(
+ () => testCachedScrollPosition(docAcc, 0, 0),
+ "Correct initial scroll position."
+ );
+ const rectAcc = findAccessibleChildByID(docAcc, "rect");
+ const rectInitialBounds = getCachedBounds(rectAcc);
+
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("square").scrollIntoView();
+ });
+
+ await waitForContentPaint(browser);
+
+ // The only content to scroll over is `square`'s top margin
+ // so our scroll offset here should be 3000px
+ await untilCacheOk(
+ () => testCachedScrollPosition(docAcc, 0, 3000),
+ "Correct scroll position after first scroll."
+ );
+
+ // Scroll rect into view, but also make it reflow so we can be sure the
+ // bounds are correct for reflowed frames.
+ await invokeContentTask(browser, [], () => {
+ const rect = content.document.getElementById("rect");
+ rect.scrollIntoView();
+ rect.style.width = "300px";
+ rect.offsetTop;
+ rect.style.width = "200px";
+ });
+
+ await waitForContentPaint(browser);
+ // We have to scroll over `square`'s top margin (3000px),
+ // `square` itself (100px), and `square`'s bottom margin (4000px).
+ // This should give us a 7100px offset.
+ await untilCacheOk(
+ () => testCachedScrollPosition(docAcc, 0, 7100),
+ "Correct final scroll position."
+ );
+ await untilCacheIs(
+ () => getCachedBounds(rectAcc),
+ rectInitialBounds,
+ "Cached relative bounds don't change when scrolling"
+ );
+ },
+ { iframe: true, remoteIframe: true }
+);
+
+/**
+ * Test scroll offset fixed-pos acc accs
+ */
+addAccessibleTask(
+ `
+ <div style="margin-top: 100px; margin-left: 75px; border: 1px solid;">
+ <div id="d" style="position:fixed;">
+ <button id="top">top</button>
+ </div>
+ </div>
+ `,
+ async function (browser, docAcc) {
+ const origTopBounds = await testBoundsWithContent(docAcc, "top", browser);
+ const origDBounds = await testBoundsWithContent(docAcc, "d", browser);
+ const e = waitForEvent(EVENT_REORDER, docAcc);
+ await invokeContentTask(browser, [], () => {
+ for (let i = 0; i < 1000; ++i) {
+ const div = content.document.createElement("div");
+ div.innerHTML = "<button>${i}</button>";
+ content.document.body.append(div);
+ }
+ });
+ await e;
+
+ await invokeContentTask(browser, [], () => {
+ // scroll to the bottom of the page
+ content.window.scrollTo(0, content.document.body.scrollHeight);
+ });
+
+ await waitForContentPaint(browser);
+
+ let newTopBounds = await testBoundsWithContent(docAcc, "top", browser);
+ let newDBounds = await testBoundsWithContent(docAcc, "d", browser);
+ is(
+ origTopBounds[0],
+ newTopBounds[0],
+ "x of fixed elem is unaffected by scrolling"
+ );
+ is(
+ origTopBounds[1],
+ newTopBounds[1],
+ "y of fixed elem is unaffected by scrolling"
+ );
+ is(
+ origTopBounds[2],
+ newTopBounds[2],
+ "width of fixed elem is unaffected by scrolling"
+ );
+ is(
+ origTopBounds[3],
+ newTopBounds[3],
+ "height of fixed elem is unaffected by scrolling"
+ );
+ is(
+ origDBounds[0],
+ newTopBounds[0],
+ "x of fixed elem container is unaffected by scrolling"
+ );
+ is(
+ origDBounds[1],
+ newDBounds[1],
+ "y of fixed elem container is unaffected by scrolling"
+ );
+ is(
+ origDBounds[2],
+ newDBounds[2],
+ "width of fixed container elem is unaffected by scrolling"
+ );
+ is(
+ origDBounds[3],
+ newDBounds[3],
+ "height of fixed container elem is unaffected by scrolling"
+ );
+
+ await invokeContentTask(browser, [], () => {
+ // remove position styling
+ content.document.getElementById("d").style = "";
+ });
+
+ await waitForContentPaint(browser);
+
+ newTopBounds = await testBoundsWithContent(docAcc, "top", browser);
+ newDBounds = await testBoundsWithContent(docAcc, "d", browser);
+ is(
+ origTopBounds[0],
+ newTopBounds[0],
+ "x of non-fixed element remains accurate."
+ );
+ ok(newTopBounds[1] < 0, "y coordinate shows item scrolled off page");
+ is(
+ origTopBounds[2],
+ newTopBounds[2],
+ "width of non-fixed element remains accurate."
+ );
+ is(
+ origTopBounds[3],
+ newTopBounds[3],
+ "height of non-fixed element remains accurate."
+ );
+ is(
+ origDBounds[0],
+ newDBounds[0],
+ "x of non-fixed container element remains accurate."
+ );
+ ok(newDBounds[1] < 0, "y coordinate shows container scrolled off page");
+ // Removing the position styling on this acc causes it to be bound by
+ // its parent's bounding box, which alters its width as a block element.
+ // We don't particularly care about width in this test, so skip it.
+ is(
+ origDBounds[3],
+ newDBounds[3],
+ "height of non-fixed container element remains accurate."
+ );
+
+ await invokeContentTask(browser, [], () => {
+ // re-add position styling
+ content.document.getElementById("d").style = "position:fixed;";
+ });
+
+ await waitForContentPaint(browser);
+
+ newTopBounds = await testBoundsWithContent(docAcc, "top", browser);
+ newDBounds = await testBoundsWithContent(docAcc, "d", browser);
+ is(
+ origTopBounds[0],
+ newTopBounds[0],
+ "x correct when position:fixed is added."
+ );
+ is(
+ origTopBounds[1],
+ newTopBounds[1],
+ "y correct when position:fixed is added."
+ );
+ is(
+ origTopBounds[2],
+ newTopBounds[2],
+ "width correct when position:fixed is added."
+ );
+ is(
+ origTopBounds[3],
+ newTopBounds[3],
+ "height correct when position:fixed is added."
+ );
+ is(
+ origDBounds[0],
+ newDBounds[0],
+ "x of container correct when position:fixed is added."
+ );
+ is(
+ origDBounds[1],
+ newDBounds[1],
+ "y of container correct when position:fixed is added."
+ );
+ is(
+ origDBounds[2],
+ newDBounds[2],
+ "width of container correct when position:fixed is added."
+ );
+ is(
+ origDBounds[3],
+ newDBounds[3],
+ "height of container correct when position:fixed is added."
+ );
+ },
+ { chrome: true, iframe: true, remoteIframe: true }
+);
+
+/**
+ * Test position: fixed for containers that would otherwise be pruned from the
+ * a11y tree.
+ */
+addAccessibleTask(
+ `
+<table id="fixed" role="presentation" style="position: fixed;">
+ <tr><th>fixed</th></tr>
+</table>
+<div id="mutate" role="presentation">mutate</div>
+<hr style="height: 200vh;">
+<p>bottom</p>
+ `,
+ async function (browser, docAcc) {
+ const fixed = findAccessibleChildByID(docAcc, "fixed");
+ ok(fixed, "fixed is accessible");
+ isnot(fixed.role, ROLE_TABLE, "fixed doesn't have ROLE_TABLE");
+ ok(!findAccessibleChildByID(docAcc, "mutate"), "mutate inaccessible");
+ info("Setting position: fixed on mutate");
+ let shown = waitForEvent(EVENT_SHOW, "mutate");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("mutate").style.position = "fixed";
+ });
+ await shown;
+ const origFixedBounds = await testBoundsWithContent(
+ docAcc,
+ "fixed",
+ browser
+ );
+ const origMutateBounds = await testBoundsWithContent(
+ docAcc,
+ "mutate",
+ browser
+ );
+ info("Scrolling to bottom of page");
+ await invokeContentTask(browser, [], () => {
+ content.window.scrollTo(0, content.document.body.scrollHeight);
+ });
+ await waitForContentPaint(browser);
+ const newFixedBounds = await testBoundsWithContent(
+ docAcc,
+ "fixed",
+ browser
+ );
+ Assert.deepEqual(
+ newFixedBounds,
+ origFixedBounds,
+ "fixed bounds are unchanged"
+ );
+ const newMutateBounds = await testBoundsWithContent(
+ docAcc,
+ "mutate",
+ browser
+ );
+ Assert.deepEqual(
+ newMutateBounds,
+ origMutateBounds,
+ "mutate bounds are unchanged"
+ );
+ },
+ { chrome: true, iframe: true, remoteIframe: true }
+);
+
+/**
+ * Test scroll offset on sticky-pos acc
+ */
+addAccessibleTask(
+ `
+ <div id="d" style="margin-top: 100px; margin-left: 75px; position:sticky; top:0px;">
+ <button id="top">top</button>
+ </div>
+ `,
+ async function (browser, docAcc) {
+ const containerBounds = await testBoundsWithContent(docAcc, "d", browser);
+ const e = waitForEvent(EVENT_REORDER, docAcc);
+ await invokeContentTask(browser, [], () => {
+ for (let i = 0; i < 1000; ++i) {
+ const div = content.document.createElement("div");
+ div.innerHTML = "<button>${i}</button>";
+ content.document.body.append(div);
+ }
+ });
+ await e;
+ for (let id of ["d", "top"]) {
+ info(`Verifying bounds for acc with ID ${id}`);
+ const origBounds = await testBoundsWithContent(docAcc, id, browser);
+
+ info("Scrolling partially");
+ await invokeContentTask(browser, [], () => {
+ // scroll some of the window
+ content.window.scrollTo(0, 50);
+ });
+
+ await waitForContentPaint(browser);
+
+ let newBounds = await testBoundsWithContent(docAcc, id, browser);
+ is(
+ origBounds[0],
+ newBounds[0],
+ `x coord of sticky element is unaffected by scrolling`
+ );
+ ok(
+ origBounds[1] > newBounds[1] && newBounds[1] >= 0,
+ "sticky element scrolled, but not off the page"
+ );
+ is(
+ origBounds[2],
+ newBounds[2],
+ `width of sticky element is unaffected by scrolling`
+ );
+ is(
+ origBounds[3],
+ newBounds[3],
+ `height of sticky element is unaffected by scrolling`
+ );
+
+ info("Scrolling to bottom");
+ await invokeContentTask(browser, [], () => {
+ // scroll to the bottom of the page
+ content.window.scrollTo(0, content.document.body.scrollHeight);
+ });
+
+ await waitForContentPaint(browser);
+
+ newBounds = await testBoundsWithContent(docAcc, id, browser);
+ is(
+ origBounds[0],
+ newBounds[0],
+ `x coord of sticky element is unaffected by scrolling`
+ );
+ // Subtract margin from container screen coords to get chrome height
+ // which is where our y pos should be
+ is(
+ newBounds[1],
+ containerBounds[1] - 100,
+ "Sticky element is top of screen"
+ );
+ is(
+ origBounds[2],
+ newBounds[2],
+ `width of sticky element is unaffected by scrolling`
+ );
+ is(
+ origBounds[3],
+ newBounds[3],
+ `height of sticky element is unaffected by scrolling`
+ );
+
+ info("Removing position style on container");
+ await invokeContentTask(browser, [], () => {
+ // remove position styling
+ content.document.getElementById("d").style =
+ "margin-top: 100px; margin-left: 75px;";
+ });
+
+ await waitForContentPaint(browser);
+
+ newBounds = await testBoundsWithContent(docAcc, id, browser);
+
+ is(
+ origBounds[0],
+ newBounds[0],
+ `x coord of non-sticky element remains accurate.`
+ );
+ ok(newBounds[1] < 0, "y coordinate shows item scrolled off page");
+
+ // Removing the position styling on this acc causes it to be bound by
+ // its parent's bounding box, which alters its width as a block element.
+ // We don't particularly care about width in this test, so skip it.
+ is(
+ origBounds[3],
+ newBounds[3],
+ `height of non-sticky element remains accurate.`
+ );
+
+ info("Adding position style on container");
+ await invokeContentTask(browser, [], () => {
+ // re-add position styling
+ content.document.getElementById("d").style =
+ "margin-top: 100px; margin-left: 75px; position:sticky; top:0px;";
+ });
+
+ await waitForContentPaint(browser);
+
+ newBounds = await testBoundsWithContent(docAcc, id, browser);
+ is(
+ origBounds[0],
+ newBounds[0],
+ `x coord of sticky element is unaffected by scrolling`
+ );
+ is(
+ newBounds[1],
+ containerBounds[1] - 100,
+ "Sticky element is top of screen"
+ );
+ is(
+ origBounds[2],
+ newBounds[2],
+ `width of sticky element is unaffected by scrolling`
+ );
+ is(
+ origBounds[3],
+ newBounds[3],
+ `height of sticky element is unaffected by scrolling`
+ );
+
+ info("Scrolling back up to test next ID");
+ await invokeContentTask(browser, [], () => {
+ // scroll some of the window
+ content.window.scrollTo(0, 0);
+ });
+ }
+ },
+ { chrome: false, iframe: false, remoteIframe: false }
+);
+
+/**
+ * Test position: sticky for containers that would otherwise be pruned from the
+ * a11y tree.
+ */
+addAccessibleTask(
+ `
+<hr style="height: 100vh;">
+<div id="stickyContainer">
+ <div id="sticky" role="presentation" style="position: sticky; top: 0px;">sticky</div>
+ <hr style="height: 100vh;">
+ <p id="stickyEnd">stickyEnd</p>
+</div>
+<div id="mutateContainer">
+ <div id="mutate" role="presentation" style="top: 0px;">mutate</div>
+ <hr style="height: 100vh;">
+ <p id="mutateEnd">mutateEnd</p>
+</div>
+ `,
+ async function (browser, docAcc) {
+ ok(findAccessibleChildByID(docAcc, "sticky"), "sticky is accessible");
+ info("Scrolling to sticky");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("sticky").scrollIntoView();
+ });
+ await waitForContentPaint(browser);
+ const origStickyBounds = await testBoundsWithContent(
+ docAcc,
+ "sticky",
+ browser
+ );
+ info("Scrolling to stickyEnd");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("stickyEnd").scrollIntoView();
+ });
+ await waitForContentPaint(browser);
+ const newStickyBounds = await testBoundsWithContent(
+ docAcc,
+ "sticky",
+ browser
+ );
+ Assert.deepEqual(
+ newStickyBounds,
+ origStickyBounds,
+ "sticky bounds are unchanged"
+ );
+
+ ok(!findAccessibleChildByID(docAcc, "mutate"), "mutate inaccessible");
+ info("Setting position: sticky on mutate");
+ let shown = waitForEvent(EVENT_SHOW, "mutate");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("mutate").style.position = "sticky";
+ });
+ await shown;
+ info("Scrolling to mutate");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("mutate").scrollIntoView();
+ });
+ await waitForContentPaint(browser);
+ const origMutateBounds = await testBoundsWithContent(
+ docAcc,
+ "mutate",
+ browser
+ );
+ info("Scrolling to mutateEnd");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("mutateEnd").scrollIntoView();
+ });
+ await waitForContentPaint(browser);
+ const newMutateBounds = await testBoundsWithContent(
+ docAcc,
+ "mutate",
+ browser
+ );
+ assertBoundsFuzzyEqual(newMutateBounds, origMutateBounds);
+ },
+ { chrome: true, iframe: true, remoteIframe: true }
+);
+
+/**
+ * Test scroll offset on non-scrollable accs
+ */
+addAccessibleTask(
+ `
+ <div id='square' style='height:100px; width: 100px; background:green;'>hello world
+ </div>
+ `,
+ async function (browser, docAcc) {
+ const square = findAccessibleChildByID(docAcc, "square");
+ await untilCacheOk(
+ () => testCachedScrollPosition(square, 0, 0, true),
+ "Square is not scrollable."
+ );
+
+ info("Adding more text content to square");
+ await invokeContentTask(browser, [], () => {
+ const s = content.document.getElementById("square");
+ s.textContent =
+ "hello world I am some text and I should overflow this container because I am very long";
+ s.offsetTop; // Flush layout.
+ });
+
+ await waitForContentPaint(browser);
+
+ await untilCacheOk(
+ () => testCachedScrollPosition(square, 0, 0, true),
+ "Square is not scrollable (still has overflow:visible)."
+ );
+
+ info("Adding overflow:auto; styling");
+ await invokeContentTask(browser, [], () => {
+ const s = content.document.getElementById("square");
+ s.setAttribute(
+ "style",
+ "overflow:auto; height:100px; width: 100px; background:green;"
+ );
+ s.offsetTop; // Flush layout.
+ });
+
+ await waitForContentPaint(browser);
+
+ await untilCacheOk(
+ () => testCachedScrollPosition(square, 0, 0),
+ "Square is scrollable."
+ );
+ },
+ { iframe: true, remoteIframe: true }
+);
diff --git a/accessible/tests/browser/scroll/browser_test_scroll_substring.js b/accessible/tests/browser/scroll/browser_test_scroll_substring.js
new file mode 100644
index 0000000000..e8426d00ca
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser_test_scroll_substring.js
@@ -0,0 +1,67 @@
+/* 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";
+
+/* import-globals-from ../../mochitest/layout.js */
+loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR });
+
+/**
+ * Test nsIAccessibleText::scrollSubstringTo.
+ */
+addAccessibleTask(
+ `
+ <style>
+ @font-face {
+ font-family: Ahem;
+ src: url(${CURRENT_CONTENT_DIR}e10s/fonts/Ahem.sjs);
+ }
+ pre {
+ font: 20px/20px Ahem;
+ height: 40px;
+ overflow-y: scroll;
+ }
+ </style>
+ <pre id="text">
+
+
+
+
+
+It's a jetpack, Michael. What could possibly go wrong?
+
+
+
+
+
+The only thing I found in the fridge was a dead dove in a bag.
+</pre>`,
+ async function (browser, docAcc) {
+ let text = findAccessibleChildByID(docAcc, "text", [nsIAccessibleText]);
+ let [, containerY, , containerHeight] = getBounds(text);
+ let getCharY = () => {
+ let objY = {};
+ text.getCharacterExtents(7, {}, objY, {}, {}, COORDTYPE_SCREEN_RELATIVE);
+ return objY.value;
+ };
+ ok(
+ containerHeight < getCharY(),
+ "Character is outside of container bounds"
+ );
+ text.scrollSubstringTo(7, 8, SCROLL_TYPE_TOP_EDGE);
+
+ await waitForContentPaint(browser);
+ await untilCacheIs(
+ getCharY,
+ containerY,
+ "Character is scrolled to top of container"
+ );
+ },
+ {
+ topLevel: true,
+ iframe: true,
+ remoteIframe: true,
+ chrome: true,
+ }
+);
diff --git a/accessible/tests/browser/scroll/browser_test_zoom_text.js b/accessible/tests/browser/scroll/browser_test_zoom_text.js
new file mode 100644
index 0000000000..4fc0a56b43
--- /dev/null
+++ b/accessible/tests/browser/scroll/browser_test_zoom_text.js
@@ -0,0 +1,145 @@
+/* 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";
+
+/* import-globals-from ../../mochitest/layout.js */
+loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR });
+
+async function runTests(browser, accDoc) {
+ await loadContentScripts(browser, {
+ script: "Layout.sys.mjs",
+ symbol: "Layout",
+ });
+
+ let paragraph = findAccessibleChildByID(accDoc, "paragraph", [
+ nsIAccessibleText,
+ ]);
+ let offset = 64; // beginning of 4th stanza
+
+ let [x /* ,y*/] = getPos(paragraph);
+ let [docX, docY] = getPos(accDoc);
+
+ paragraph.scrollSubstringToPoint(
+ offset,
+ offset,
+ COORDTYPE_SCREEN_RELATIVE,
+ docX,
+ docY
+ );
+
+ await waitForContentPaint(browser);
+ testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE);
+
+ await SpecialPowers.spawn(browser, [], () => {
+ content.Layout.zoomDocument(content.document, 2.0);
+ });
+
+ paragraph = findAccessibleChildByID(accDoc, "paragraph2", [
+ nsIAccessibleText,
+ ]);
+ offset = 52; // // beginning of 4th stanza
+ [x /* ,y*/] = getPos(paragraph);
+ paragraph.scrollSubstringToPoint(
+ offset,
+ offset,
+ COORDTYPE_SCREEN_RELATIVE,
+ docX,
+ docY
+ );
+
+ await waitForContentPaint(browser);
+ testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE);
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask(
+ `
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br><hr>
+ <p id='paragraph'>
+ Пошел котик на торжок<br>
+ Купил котик пирожок<br>
+ Пошел котик на улочку<br>
+ Купил котик булочку<br>
+ </p>
+ <hr><br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br><hr>
+ <p id='paragraph2'>
+ Самому ли съесть<br>
+ Либо Сашеньке снесть<br>
+ Я и сам укушу<br>
+ Я и Сашеньке снесу<br>
+ </p>
+ <hr><br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>`,
+ runTests
+);
diff --git a/accessible/tests/browser/scroll/head.js b/accessible/tests/browser/scroll/head.js
new file mode 100644
index 0000000000..afc50984bd
--- /dev/null
+++ b/accessible/tests/browser/scroll/head.js
@@ -0,0 +1,18 @@
+/* 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.
+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 }
+);