Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* 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";
Services.scriptloader.loadSubScript(
this
);
// On Windows, synthesizeNativeTouch() resolves when
// nsWindow::SynthesizeNativeTouchPoint() returns after InjectTouchInput(),
// before nsWindow::OnTouch() handles the resulting WM_TOUCH. Wait until the
// page receives the touch event so that the checks below see it. Otherwise,
// sometimes touches are coalescenced.
async function synthesizeNativeTouchAndWaitForContentEvent(
aBrowser,
aX,
aY,
aType,
aEventType
) {
const received = BrowserTestUtils.waitForContentEvent(aBrowser, aEventType);
await synthesizeNativeTouch(aBrowser, aX, aY, aType);
await received;
}
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"],
["browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"],
["widget.disable-swipe-tracker", false],
["widget.swipe.touchscreen.enabled", true],
["widget.swipe.velocity-twitch-tolerance", 0.0000001],
["widget.swipe.success-velocity-contribution", 0.0],
["widget.swipe.pixel-size", 100.0],
// On Windows, the OS sometimes delays delivering an injected
// touch-down by hundreds of milliseconds somehow, and lifts an
// injected contact that gets no further frames (see
// InjectTouchInput() official doc on press and hold). This
// test has to check the page's events after each touch before
// sending the next one. So this test dispatches the touches
// directly through nsIWidget::DispatchTouchInput() instead.
["apz.test.fails_with_native_injection", AppConstants.platform == "win"],
["apz.touch_start_tolerance", 0.0],
["apz.touch_move_tolerance", 0.0],
["apz.overscroll.enabled", true],
["apz.test.logging_enabled", true],
// The touchstart can turn into a long-tap, and thus into a context menu
// that cancels the pointer, if the test is slow to send the next move.
["ui.click_hold_context_menus.delay", 10000],
],
});
});
add_task(async function test_completed_back_swipe_touch_events() {
const URL_ROOT = getRootDirectory(gTestPath).replace(
);
const firstPage = URL_ROOT + "helper_swipe_gesture.html";
const secondPage = firstPage + "?second";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, firstPage);
const interceptNavigation = event => {
// Keep the page alive to check events after the finger lifts.
event.preventDefault();
event.stopImmediatePropagation();
};
window.addEventListener("MozSwipeGesture", interceptNavigation, true);
registerCleanupFunction(() =>
window.removeEventListener("MozSwipeGesture", interceptNavigation, true)
);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, secondPage);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
ok(
tab.linkedBrowser.webNavigation.canGoBack,
"History permits back navigation"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.promiseApzFlushedRepaints();
const events = [];
content.wrappedJSObject.swipeEvents = events;
for (const type of ["touchstart", "touchmove", "touchend", "touchcancel"]) {
content.document.addEventListener(type, event => {
events.push(event.type);
});
}
await content.wrappedJSObject.promiseApzFlushedRepaints();
});
const { TOUCH_CONTACT, TOUCH_REMOVE } = SpecialPowers.DOMWindowUtils;
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchstart"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart"],
"Touching the screen starts the touch sequence"
);
});
const started = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureStart",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
200,
100,
TOUCH_CONTACT,
"touchmove"
);
await started;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove"],
"The page receives touchmove when swipe tracking starts"
);
});
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
300,
100,
TOUCH_CONTACT,
"touchmove"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove", "touchmove"],
"Touch events continue during the swipe"
);
});
const ended = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureEnd",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
300,
100,
TOUCH_REMOVE,
"touchend"
);
await ended;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove", "touchmove", "touchend"],
"Lifting the finger ends the completed swipe with touchend"
);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_canceled_back_swipe_touch_events() {
const URL_ROOT = getRootDirectory(gTestPath).replace(
);
const firstPage = URL_ROOT + "helper_swipe_gesture.html";
const secondPage = firstPage + "?second";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, firstPage);
const interceptNavigation = event => {
// Keep the page alive to check events after the finger lifts.
event.preventDefault();
event.stopImmediatePropagation();
};
window.addEventListener("MozSwipeGesture", interceptNavigation, true);
registerCleanupFunction(() =>
window.removeEventListener("MozSwipeGesture", interceptNavigation, true)
);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, secondPage);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
ok(
tab.linkedBrowser.webNavigation.canGoBack,
"History permits back navigation"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.promiseApzFlushedRepaints();
const events = [];
content.wrappedJSObject.swipeEvents = events;
for (const type of ["touchstart", "touchmove", "touchend", "touchcancel"]) {
content.document.addEventListener(type, event => {
events.push(event.type);
});
}
await content.wrappedJSObject.promiseApzFlushedRepaints();
});
const { TOUCH_CONTACT, TOUCH_REMOVE } = SpecialPowers.DOMWindowUtils;
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchstart"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart"],
"Touching the screen starts the touch sequence"
);
});
const started = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureStart",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
200,
100,
TOUCH_CONTACT,
"touchmove"
);
await started;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove"],
"The page receives touchmove when swipe tracking starts"
);
});
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchmove"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove", "touchmove"],
"Touch events continue during the swipe"
);
});
const ended = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureEnd",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_REMOVE,
"touchend"
);
await ended;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["touchstart", "touchmove", "touchmove", "touchend"],
"Lifting the finger ends the canceled swipe with touchend"
);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_completed_back_swipe_pointer_events() {
const URL_ROOT = getRootDirectory(gTestPath).replace(
);
const firstPage = URL_ROOT + "helper_swipe_gesture.html";
const secondPage = firstPage + "?second";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, firstPage);
const interceptNavigation = event => {
// Keep the page alive to check events after the finger lifts.
event.preventDefault();
event.stopImmediatePropagation();
};
window.addEventListener("MozSwipeGesture", interceptNavigation, true);
registerCleanupFunction(() =>
window.removeEventListener("MozSwipeGesture", interceptNavigation, true)
);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, secondPage);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
ok(
tab.linkedBrowser.webNavigation.canGoBack,
"History permits back navigation"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.promiseApzFlushedRepaints();
const events = [];
content.wrappedJSObject.swipeEvents = events;
for (const type of [
"pointerdown",
"pointermove",
"pointercancel",
"pointerout",
"pointerup",
]) {
content.document.addEventListener(type, event => {
if (event.pointerType != "touch") {
return;
}
events.push(event.type);
});
}
});
const { TOUCH_CONTACT, TOUCH_REMOVE } = SpecialPowers.DOMWindowUtils;
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchstart"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown"],
"Touching the screen starts the pointer sequence"
);
});
const started = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureStart",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
200,
100,
TOUCH_CONTACT,
"touchmove"
);
await started;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"Swipe tracking cancels the pointer after pointermove"
);
});
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
300,
100,
TOUCH_CONTACT,
"touchmove"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"The canceled pointer receives no further moves"
);
});
const ended = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureEnd",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
300,
100,
TOUCH_REMOVE,
"touchend"
);
await ended;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"Lifting the finger does not dispatch pointerup after pointercancel"
);
});
BrowserTestUtils.removeTab(tab);
});
add_task(async function test_canceled_back_swipe_pointer_events() {
const URL_ROOT = getRootDirectory(gTestPath).replace(
);
const firstPage = URL_ROOT + "helper_swipe_gesture.html";
const secondPage = firstPage + "?second";
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, firstPage);
const interceptNavigation = event => {
// Keep the page alive to check events after the finger lifts.
event.preventDefault();
event.stopImmediatePropagation();
};
window.addEventListener("MozSwipeGesture", interceptNavigation, true);
registerCleanupFunction(() =>
window.removeEventListener("MozSwipeGesture", interceptNavigation, true)
);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, secondPage);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, secondPage);
ok(
tab.linkedBrowser.webNavigation.canGoBack,
"History permits back navigation"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
await content.wrappedJSObject.promiseApzFlushedRepaints();
const events = [];
content.wrappedJSObject.swipeEvents = events;
for (const type of [
"pointerdown",
"pointermove",
"pointercancel",
"pointerout",
"pointerup",
]) {
content.document.addEventListener(type, event => {
if (event.pointerType != "touch") {
return;
}
events.push(event.type);
});
}
});
const { TOUCH_CONTACT, TOUCH_REMOVE } = SpecialPowers.DOMWindowUtils;
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchstart"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown"],
"Touching the screen starts the pointer sequence"
);
});
const started = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureStart",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
200,
100,
TOUCH_CONTACT,
"touchmove"
);
await started;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"Swipe tracking cancels the pointer after pointermove"
);
});
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_CONTACT,
"touchmove"
);
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"The canceled pointer receives no further moves"
);
});
const ended = BrowserTestUtils.waitForEvent(
window,
"MozSwipeGestureEnd",
true
);
await synthesizeNativeTouchAndWaitForContentEvent(
tab.linkedBrowser,
100,
100,
TOUCH_REMOVE,
"touchend"
);
await ended;
await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
Assert.deepEqual(
Array.from(content.wrappedJSObject.swipeEvents),
["pointerdown", "pointermove", "pointercancel", "pointerout"],
"Lifting the finger does not dispatch pointerup after pointercancel"
);
});
BrowserTestUtils.removeTab(tab);
});