summaryrefslogtreecommitdiffstats
path: root/widget/tests/browser/browser_test_swipe_gesture.js
diff options
context:
space:
mode:
Diffstat (limited to 'widget/tests/browser/browser_test_swipe_gesture.js')
-rw-r--r--widget/tests/browser/browser_test_swipe_gesture.js1275
1 files changed, 1275 insertions, 0 deletions
diff --git a/widget/tests/browser/browser_test_swipe_gesture.js b/widget/tests/browser/browser_test_swipe_gesture.js
new file mode 100644
index 0000000000..e66e2f10b9
--- /dev/null
+++ b/widget/tests/browser/browser_test_swipe_gesture.js
@@ -0,0 +1,1275 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ this
+);
+
+async function waitForWhile() {
+ await new Promise(resolve => {
+ requestIdleCallback(resolve, { timeout: 300 });
+ });
+ await new Promise(r => requestAnimationFrame(r));
+}
+
+requestLongerTimeout(2);
+
+add_task(async () => {
+ // Set the default values for an OS that supports swipe to nav, except for
+ // whole-page-pixel-size which varies by OS, we vary it in differente tests
+ // in this file.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ // Set the velocity-contribution to 0 so we can exactly control the
+ // values in the swipe tracker via the delta in the events that we send.
+ ["widget.swipe.success-velocity-contribution", 0.0],
+ ["widget.swipe.whole-page-pixel-size", 550.0],
+ ],
+ });
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let wheelEventCount = 0;
+ tab.linkedBrowser.addEventListener("wheel", () => {
+ wheelEventCount++;
+ });
+
+ // Send a pan that starts a navigate back but doesn't have enough delta to do
+ // anything. Don't send the pan end because we want to check the opacity
+ // before the MSD animation in SwipeTracker starts which can temporarily put
+ // us at 1 opacity.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 0.9);
+ await panLeftToRightUpdate(tab.linkedBrowser, 100, 100, 0.9);
+
+ // Check both getComputedStyle instead of element.style.opacity because we use a transition on the opacity.
+ let computedOpacity = window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("opacity");
+ is(computedOpacity, "1", "opacity of prevbox is 1");
+ let opacity = gHistorySwipeAnimation._prevBox.style.opacity;
+ is(opacity, "", "opacity style isn't explicitly set");
+
+ const isTranslatingIcon =
+ Services.prefs.getIntPref(
+ "browser.swipe.navigation-icon-start-position",
+ 0
+ ) != 0 ||
+ Services.prefs.getIntPref(
+ "browser.swipe.navigation-icon-end-position",
+ 0
+ ) != 0;
+ if (isTranslatingIcon != 0) {
+ isnot(
+ window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("translate"),
+ "none",
+ "translate of prevbox is not `none` during gestures"
+ );
+ }
+
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 0.9);
+
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+
+ // Try to navigate backward.
+ wheelEventCount = 0;
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ // The element.style opacity will be 0 because we set it to 0 on successful navigation, however
+ // we have a tranisition on it so the computed style opacity will still be 1 because the transition hasn't started yet.
+ computedOpacity = window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("opacity");
+ ok(computedOpacity == 1, "computed opacity of prevbox is 1");
+ opacity = gHistorySwipeAnimation._prevBox.style.opacity;
+ ok(opacity == 0, "element.style opacity of prevbox 0");
+
+ if (isTranslatingIcon) {
+ // We don't have a transition for translate property so that we still have
+ // some amount of translate.
+ isnot(
+ window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("translate"),
+ "none",
+ "translate of prevbox is not `none` during the opacity transition"
+ );
+ }
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// Same test as above but whole-page-pixel-size is increased and the multipliers passed to panLeftToRight correspondingly increased.
+add_task(async () => {
+ // Set the default values for an OS that supports swipe to nav, except for
+ // whole-page-pixel-size which varies by OS, we vary it in differente tests
+ // in this file.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ // Set the velocity-contribution to 0 so we can exactly control the
+ // values in the swipe tracker via the delta in the events that we send.
+ ["widget.swipe.success-velocity-contribution", 0.0],
+ ["widget.swipe.whole-page-pixel-size", 1100.0],
+ ],
+ });
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let wheelEventCount = 0;
+ tab.linkedBrowser.addEventListener("wheel", () => {
+ wheelEventCount++;
+ });
+
+ // Send a pan that starts a navigate back but doesn't have enough delta to do
+ // anything. Don't send the pan end because we want to check the opacity
+ // before the MSD animation in SwipeTracker starts which can temporarily put
+ // us at 1 opacity.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 1.8);
+ await panLeftToRightUpdate(tab.linkedBrowser, 100, 100, 1.8);
+
+ // Check both getComputedStyle instead of element.style.opacity because we use a transition on the opacity.
+ let computedOpacity = window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("opacity");
+ is(computedOpacity, "1", "opacity of prevbox is 1");
+ let opacity = gHistorySwipeAnimation._prevBox.style.opacity;
+ is(opacity, "", "opacity style isn't explicitly set");
+
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 1.8);
+
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+
+ // Try to navigate backward.
+ wheelEventCount = 0;
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 2);
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ // The element.style opacity will be 0 because we set it to 0 on successful navigation, however
+ // we have a tranisition on it so the computed style opacity will still be 1 because the transition hasn't started yet.
+ computedOpacity = window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("opacity");
+ ok(computedOpacity == 1, "computed opacity of prevbox is 1");
+ opacity = gHistorySwipeAnimation._prevBox.style.opacity;
+ ok(opacity == 0, "element.style opacity of prevbox 0");
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ // Set the default values for an OS that supports swipe to nav, except for
+ // whole-page-pixel-size which varies by OS, we vary it in different tests
+ // in this file.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ // Set the velocity-contribution to 1 (default 0.05f) so velocity is a
+ // large contribution to the success value in SwipeTracker.cpp so it
+ // pushes us into success territory without going into success territory
+ // purely from th deltas.
+ ["widget.swipe.success-velocity-contribution", 2.0],
+ ["widget.swipe.whole-page-pixel-size", 550.0],
+ ],
+ });
+
+ async function runTest() {
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let wheelEventCount = 0;
+ tab.linkedBrowser.addEventListener("wheel", () => {
+ wheelEventCount++;
+ });
+
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let startTime = performance.now();
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 0.2);
+ let endTime = performance.now();
+
+ // If sending the events took too long then we might not have been able
+ // to generate enough velocity.
+ // The value 230 was picked based on try runs, in particular test verify
+ // runs on mac were the long pole, and when we get times near this we can
+ // still achieve the required velocity.
+ if (endTime - startTime > 230) {
+ BrowserTestUtils.removeTab(tab);
+ return false;
+ }
+
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ // The element.style opacity will be 0 because we set it to 0 on successful navigation, however
+ // we have a tranisition on it so the computed style opacity will still be 1 because the transition hasn't started yet.
+ let computedOpacity = window
+ .getComputedStyle(gHistorySwipeAnimation._prevBox)
+ .getPropertyValue("opacity");
+ ok(computedOpacity == 1, "computed opacity of prevbox is 1");
+ let opacity = gHistorySwipeAnimation._prevBox.style.opacity;
+ ok(opacity == 0, "element.style opacity of prevbox 0");
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ BrowserTestUtils.removeTab(tab);
+
+ return true;
+ }
+
+ let numTries = 15;
+ while (numTries > 0) {
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(resolve => requestIdleCallback(resolve));
+ await new Promise(r => requestAnimationFrame(r));
+
+ // runTest return value indicates if test was able to run to the end.
+ if (await runTest()) {
+ break;
+ }
+ numTries--;
+ }
+ ok(numTries > 0, "never ran the test");
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ // Set the default values for an OS that supports swipe to nav, except for
+ // whole-page-pixel-size which varies by OS, we vary it in differente tests
+ // in this file.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ // Set the velocity-contribution to 0 so we can exactly control the
+ // values in the swipe tracker via the delta in the events that we send.
+ ["widget.swipe.success-velocity-contribution", 0.0],
+ ["widget.swipe.whole-page-pixel-size", 550.0],
+ ],
+ });
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 2);
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ while (
+ gHistorySwipeAnimation._prevBox != null ||
+ gHistorySwipeAnimation._nextBox != null
+ ) {
+ await new Promise(r => requestAnimationFrame(r));
+ }
+
+ ok(
+ gHistorySwipeAnimation._prevBox == null &&
+ gHistorySwipeAnimation._nextBox == null
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ // Set the velocity-contribution to 0 so we can exactly control the
+ // values in the swipe tracker via the delta in the events that we send.
+ ["widget.swipe.success-velocity-contribution", 0.0],
+ ["widget.swipe.whole-page-pixel-size", 550.0],
+ ],
+ });
+
+ function swipeGestureEndPromise() {
+ return new Promise(resolve => {
+ let promiseObserver = {
+ handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "MozSwipeGestureEnd":
+ gBrowser.tabbox.removeEventListener(
+ "MozSwipeGestureEnd",
+ promiseObserver,
+ true
+ );
+ resolve();
+ break;
+ }
+ },
+ };
+ gBrowser.tabbox.addEventListener(
+ "MozSwipeGestureEnd",
+ promiseObserver,
+ true
+ );
+ });
+ }
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let numSwipeGestureEndEvents = 0;
+ var anObserver = {
+ handleEvent(aEvent) {
+ switch (aEvent.type) {
+ case "MozSwipeGestureEnd":
+ numSwipeGestureEndEvents++;
+ break;
+ }
+ },
+ };
+
+ gBrowser.tabbox.addEventListener("MozSwipeGestureEnd", anObserver, true);
+
+ let gestureEndPromise = swipeGestureEndPromise();
+
+ is(
+ numSwipeGestureEndEvents,
+ 0,
+ "expected no MozSwipeGestureEnd got " + numSwipeGestureEndEvents
+ );
+
+ // Send a pan that starts a navigate back but doesn't have enough delta to do
+ // anything.
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 0.9);
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+ // end event comes after a swipe that does not navigate
+ await gestureEndPromise;
+ is(
+ numSwipeGestureEndEvents,
+ 1,
+ "expected one MozSwipeGestureEnd got " + numSwipeGestureEndEvents
+ );
+
+ // Try to navigate backward.
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+
+ gestureEndPromise = swipeGestureEndPromise();
+
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ await gestureEndPromise;
+
+ is(
+ numSwipeGestureEndEvents,
+ 2,
+ "expected one MozSwipeGestureEnd got " + (numSwipeGestureEndEvents - 1)
+ );
+
+ gBrowser.tabbox.removeEventListener("MozSwipeGestureEnd", anObserver, true);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ // success-velocity-contribution is very high and whole-page-pixel-size is
+ // very low so that one swipe goes over the threshold asap.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 999999.0],
+ ["widget.swipe.whole-page-pixel-size", 1.0],
+ ],
+ });
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ // Navigate backward.
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 100);
+
+ ok(gHistorySwipeAnimation._prevBox != null, "should have prevbox");
+ let transitionCancelPromise = new Promise(resolve => {
+ gHistorySwipeAnimation._prevBox.addEventListener(
+ "transitioncancel",
+ event => {
+ if (
+ event.propertyName == "opacity" &&
+ event.target == gHistorySwipeAnimation._prevBox
+ ) {
+ resolve();
+ }
+ },
+ { once: true }
+ );
+ });
+ let transitionStartPromise = new Promise(resolve => {
+ gHistorySwipeAnimation._prevBox.addEventListener(
+ "transitionstart",
+ event => {
+ if (
+ event.propertyName == "opacity" &&
+ event.target == gHistorySwipeAnimation._prevBox
+ ) {
+ resolve();
+ }
+ },
+ { once: true }
+ );
+ });
+
+ await panLeftToRightUpdate(tab.linkedBrowser, 100, 100, 100);
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 100);
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ await Promise.any([transitionStartPromise, transitionCancelPromise]);
+
+ await TestUtils.waitForCondition(() => {
+ return (
+ gHistorySwipeAnimation._prevBox == null &&
+ gHistorySwipeAnimation._nextBox == null
+ );
+ });
+
+ // Navigate forward and check the forward navigation icon box state.
+ startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ secondPage
+ );
+ stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ secondPage
+ );
+
+ await panRightToLeftBegin(tab.linkedBrowser, 100, 100, 100);
+
+ ok(gHistorySwipeAnimation._nextBox != null, "should have nextbox");
+ transitionCancelPromise = new Promise(resolve => {
+ gHistorySwipeAnimation._nextBox.addEventListener(
+ "transitioncancel",
+ event => {
+ if (
+ event.propertyName == "opacity" &&
+ event.target == gHistorySwipeAnimation._nextBox
+ ) {
+ resolve();
+ }
+ }
+ );
+ });
+ transitionStartPromise = new Promise(resolve => {
+ gHistorySwipeAnimation._nextBox.addEventListener(
+ "transitionstart",
+ event => {
+ if (
+ event.propertyName == "opacity" &&
+ event.target == gHistorySwipeAnimation._nextBox
+ ) {
+ resolve();
+ }
+ }
+ );
+ });
+
+ await panRightToLeftUpdate(tab.linkedBrowser, 100, 100, 100);
+ await panRightToLeftEnd(tab.linkedBrowser, 100, 100, 100);
+
+ // Make sure the gesture triggered going forward to the next page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoBack);
+
+ await Promise.any([transitionStartPromise, transitionCancelPromise]);
+
+ await TestUtils.waitForCondition(() => {
+ return (
+ gHistorySwipeAnimation._nextBox == null &&
+ gHistorySwipeAnimation._prevBox == null
+ );
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// A simple test case on RTL.
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ["intl.l10n.pseudo", "bidi"],
+ ],
+ });
+
+ const newWin = await BrowserTestUtils.openNewBrowserWindow();
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ newWin.gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(newWin.gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!newWin.gBrowser.webNavigation.canGoForward);
+
+ // Make sure that our gesture support stuff has been initialized in the new
+ // browser window.
+ await TestUtils.waitForCondition(() => {
+ return newWin.gHistorySwipeAnimation.active;
+ });
+
+ // Try to navigate backward.
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ await panRightToLeft(tab.linkedBrowser, 100, 100, 1);
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(newWin.gBrowser.webNavigation.canGoForward);
+
+ // Now try to navigate forward again.
+ startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ secondPage
+ );
+ stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ secondPage
+ );
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(newWin.gBrowser.webNavigation.canGoBack);
+
+ await BrowserTestUtils.closeWindow(newWin);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ["apz.overscroll.enabled", true],
+ ["apz.test.logging_enabled", true],
+ ],
+ });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about",
+ true /* waitForLoad */
+ );
+
+ const URL_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://mochi.test:8888/"
+ );
+ BrowserTestUtils.loadURIString(
+ tab.linkedBrowser,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ // Set `overscroll-behavior-x: contain` and flush it.
+ content.document.documentElement.style.overscrollBehaviorX = "contain";
+ content.document.documentElement.getBoundingClientRect();
+ await content.wrappedJSObject.promiseApzFlushedRepaints();
+ });
+
+ // Start a pan gesture but keep touching.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 2);
+
+ // Flush APZ pending requests to make sure the pan gesture has been processed.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await content.wrappedJSObject.promiseApzFlushedRepaints();
+ });
+
+ const isOverscrolled = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ const scrollId = SpecialPowers.DOMWindowUtils.getViewId(
+ content.document.scrollingElement
+ );
+ const data = SpecialPowers.DOMWindowUtils.getCompositorAPZTestData();
+ return data.additionalData.some(entry => {
+ return (
+ entry.key == scrollId &&
+ entry.value.split(",").includes("overscrolled")
+ );
+ });
+ }
+ );
+
+ ok(isOverscrolled, "The root scroller should have overscrolled");
+
+ // Finish the pan gesture.
+ await panLeftToRightUpdate(tab.linkedBrowser, 100, 100, 2);
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 2);
+
+ // And wait a while to give a chance to navigate.
+ await waitForWhile();
+
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, URL_ROOT + "helper_swipe_gesture.html");
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// A test case to make sure the short circuit path for swipe-to-navigations in
+// APZ works, i.e. cases where we know for sure that the target APZC for a given
+// pan-start event isn't scrollable in the pan-start event direction.
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["apz.overscroll.enabled", true],
+ ],
+ });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about",
+ true /* waitForLoad */
+ );
+
+ const URL_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://mochi.test:8888/"
+ );
+ BrowserTestUtils.loadURIString(
+ tab.linkedBrowser,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+
+ // Make sure the content can allow both of overscrolling and
+ // swipe-to-navigations.
+ const overscrollBehaviorX = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return content.window.getComputedStyle(content.document.documentElement)
+ .overscrollBehaviorX;
+ }
+ );
+ is(overscrollBehaviorX, "auto");
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+
+ // Start a pan gesture but keep touching.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 2);
+
+ // The above pan event should invoke a SwipeGestureStart event immediately so
+ // that the swipe-to-navigation icon box should be uncollapsed to show it.
+ ok(!gHistorySwipeAnimation._prevBox.collapsed);
+
+ // Finish the pan gesture, i.e. sending a pan-end event, otherwise a new
+ // pan-start event in the next will also generate a pan-interrupt event which
+ // will break the test.
+ await panLeftToRightUpdate(tab.linkedBrowser, 100, 100, 2);
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 2);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ["apz.overscroll.enabled", true],
+ ["apz.test.logging_enabled", true],
+ ],
+ });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about",
+ true /* waitForLoad */
+ );
+
+ const URL_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://mochi.test:8888/"
+ );
+ BrowserTestUtils.loadURIString(
+ tab.linkedBrowser,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+
+ // Start a pan gesture but keep touching.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 2);
+
+ // Flush APZ pending requests to make sure the pan gesture has been processed.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await content.wrappedJSObject.promiseApzFlushedRepaints();
+ });
+
+ const isOverscrolled = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ const scrollId = SpecialPowers.DOMWindowUtils.getViewId(
+ content.document.scrollingElement
+ );
+ const data = SpecialPowers.DOMWindowUtils.getCompositorAPZTestData();
+ return data.additionalData.some(entry => {
+ return entry.key == scrollId && entry.value.includes("overscrolled");
+ });
+ }
+ );
+
+ ok(!isOverscrolled, "The root scroller should not have overscrolled");
+
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 0);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ],
+ });
+
+ // Load three pages and go to the second page so that it can be navigated
+ // to both back and forward.
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about",
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, "about:mozilla");
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ "about:mozilla"
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, "about:home");
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ "about:home"
+ );
+
+ gBrowser.goBack();
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ "about:mozilla"
+ );
+
+ // Make sure we can go back and go forward.
+ ok(gBrowser.webNavigation.canGoBack);
+ ok(gBrowser.webNavigation.canGoForward);
+
+ // Start a history back pan gesture but keep touching.
+ await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 1);
+
+ ok(
+ !gHistorySwipeAnimation._prevBox.collapsed,
+ "The icon box for the previous navigation should NOT be collapsed"
+ );
+ ok(
+ gHistorySwipeAnimation._nextBox.collapsed,
+ "The icon box for the next navigation should be collapsed"
+ );
+
+ // Pan back to the opposite direction so that the gesture should be cancelled.
+ // eslint-disable-next-line no-undef
+ await NativePanHandler.promiseNativePanEvent(
+ tab.linkedBrowser,
+ 100,
+ 100,
+ // eslint-disable-next-line no-undef
+ NativePanHandler.delta,
+ 0,
+ // eslint-disable-next-line no-undef
+ NativePanHandler.updatePhase
+ );
+
+ ok(
+ gHistorySwipeAnimation._prevBox.collapsed,
+ "The icon box for the previous navigation should be collapsed"
+ );
+ ok(
+ gHistorySwipeAnimation._nextBox.collapsed,
+ "The icon box for the next navigation should be collapsed"
+ );
+
+ await panLeftToRightEnd(tab.linkedBrowser, 100, 100, 0);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ["apz.overscroll.enabled", true],
+ ["apz.overscroll.damping", 5.0],
+ ["apz.content_response_timeout", 0],
+ ],
+ });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about",
+ true /* waitForLoad */
+ );
+
+ const URL_ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://mochi.test:8888/"
+ );
+
+ // Load a horizontal scrollable content.
+ BrowserTestUtils.loadURIString(
+ tab.linkedBrowser,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false /* includeSubFrames */,
+ URL_ROOT + "helper_swipe_gesture.html"
+ );
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+
+ // Shift the horizontal scroll position slightly to make the content
+ // overscrollable.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.documentElement.scrollLeft = 1;
+ content.document.documentElement.getBoundingClientRect();
+ await content.wrappedJSObject.promiseApzFlushedRepaints();
+ });
+
+ // Swipe horizontally to overscroll.
+ await panLeftToRight(tab.linkedBrowser, 1, 100, 1);
+
+ // Swipe again over the overscroll gutter.
+ await panLeftToRight(tab.linkedBrowser, 1, 100, 1);
+
+ // Wait the overscroll gutter is restored.
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ // For some reasons using functions in apz_test_native_event_utils.js
+ // sometimes causes "TypeError content.wrappedJSObject.XXXX is not a
+ // function" error, so we observe "APZ:TransformEnd" instead of using
+ // promiseTransformEnd().
+ await new Promise((resolve, reject) => {
+ SpecialPowers.Services.obs.addObserver(function observer(
+ subject,
+ topic,
+ data
+ ) {
+ try {
+ SpecialPowers.Services.obs.removeObserver(observer, topic);
+ resolve([subject, data]);
+ } catch (ex) {
+ SpecialPowers.Services.obs.removeObserver(observer, topic);
+ reject(ex);
+ }
+ },
+ "APZ:TransformEnd");
+ });
+ });
+
+ // Set up an APZ aware event listener and...
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.documentElement.addEventListener("wheel", e => {}, {
+ passive: false,
+ });
+ await content.wrappedJSObject.promiseApzFlushedRepaints();
+ });
+
+ // Try to swipe back again without overscrolling to make sure swipe-navigation
+ // works with the APZ aware event listener.
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ "about:about"
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ "about:about"
+ );
+
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+// NOTE: This test listens wheel events so that it causes an overscroll issue
+// (bug 1800022). To avoid the bug, we need to run this test case at the end
+// of this file.
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
+ ["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
+ ["widget.disable-swipe-tracker", false],
+ ["widget.swipe.velocity-twitch-tolerance", 0.0000001],
+ ["widget.swipe.success-velocity-contribution", 0.5],
+ ],
+ });
+
+ const firstPage = "about:about";
+ const secondPage = "about:mozilla";
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ firstPage,
+ true /* waitForLoad */
+ );
+
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, secondPage);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
+
+ // Make sure we can go back to the previous page.
+ ok(gBrowser.webNavigation.canGoBack);
+ // and we cannot go forward to the next page.
+ ok(!gBrowser.webNavigation.canGoForward);
+
+ let wheelEventCount = 0;
+ tab.linkedBrowser.addEventListener("wheel", () => {
+ wheelEventCount++;
+ });
+
+ // Try to navigate forward.
+ await panRightToLeft(tab.linkedBrowser, 100, 100, 1);
+ // NOTE: The last endPhase shouldn't fire a wheel event since
+ // its delta is zero.
+ is(wheelEventCount, 2, "Received 2 wheel events");
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+
+ // Try to navigate backward.
+ wheelEventCount = 0;
+ let startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ firstPage
+ );
+ let stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ firstPage
+ );
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+ // NOTE: We only get a wheel event for the beginPhase, rest of events have
+ // been captured by the swipe gesture module.
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ // Make sure the gesture triggered going back to the previous page.
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoForward);
+
+ // Now try to navigate forward again.
+ wheelEventCount = 0;
+ startLoadingPromise = BrowserTestUtils.browserStarted(
+ tab.linkedBrowser,
+ secondPage
+ );
+ stoppedLoadingPromise = BrowserTestUtils.browserStopped(
+ tab.linkedBrowser,
+ secondPage
+ );
+ await panRightToLeft(tab.linkedBrowser, 100, 100, 1);
+ is(wheelEventCount, 1, "Received a wheel event");
+
+ await Promise.all([startLoadingPromise, stoppedLoadingPromise]);
+
+ ok(gBrowser.webNavigation.canGoBack);
+
+ // Now try to navigate backward again but with preventDefault-ed event
+ // handler.
+ wheelEventCount = 0;
+ let wheelEventListener = event => {
+ event.preventDefault();
+ };
+ tab.linkedBrowser.addEventListener("wheel", wheelEventListener);
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+ is(wheelEventCount, 3, "Received all wheel events");
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+
+ // Now drop the event handler and disable the swipe tracker and try to swipe
+ // again.
+ wheelEventCount = 0;
+ tab.linkedBrowser.removeEventListener("wheel", wheelEventListener);
+ await SpecialPowers.pushPrefEnv({
+ set: [["widget.disable-swipe-tracker", true]],
+ });
+
+ await panLeftToRight(tab.linkedBrowser, 100, 100, 1);
+ is(wheelEventCount, 3, "Received all wheel events");
+
+ await waitForWhile();
+ // Make sure any navigation didn't happen.
+ is(tab.linkedBrowser.currentURI.spec, secondPage);
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});