import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,261 @@
// Utilities for synthesizing of native events.
function getPlatform() {
if (navigator.platform.indexOf("Win") == 0) {
return "windows";
}
if (navigator.platform.indexOf("Mac") == 0) {
return "mac";
}
// Check for Android before Linux
if (navigator.appVersion.indexOf("Android") >= 0) {
return "android"
}
if (navigator.platform.indexOf("Linux") == 0) {
return "linux";
}
return "unknown";
}
function nativeVerticalWheelEventMsg() {
switch (getPlatform()) {
case "windows": return 0x020A; // WM_MOUSEWHEEL
case "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
}
throw "Native wheel events not supported on platform " + getPlatform();
}
function nativeHorizontalWheelEventMsg() {
switch (getPlatform()) {
case "windows": return 0x020E; // WM_MOUSEHWHEEL
case "mac": return 0; // value is unused, can be anything
case "linux": return 4; // value is unused, pass GDK_SCROLL_SMOOTH anyway
}
throw "Native wheel events not supported on platform " + getPlatform();
}
// Given a pixel scrolling delta, converts it to the platform's native units.
function nativeScrollUnits(aElement, aDimen) {
switch (getPlatform()) {
case "linux": {
// GTK deltas are treated as line height divided by 3 by gecko.
var targetWindow = aElement.ownerDocument.defaultView;
var lineHeight = targetWindow.getComputedStyle(aElement)["font-size"];
return aDimen / (parseInt(lineHeight) * 3);
}
}
return aDimen;
}
function nativeMouseDownEventMsg() {
switch (getPlatform()) {
case "windows": return 2; // MOUSEEVENTF_LEFTDOWN
case "mac": return 1; // NSLeftMouseDown
case "linux": return 4; // GDK_BUTTON_PRESS
case "android": return 5; // ACTION_POINTER_DOWN
}
throw "Native mouse-down events not supported on platform " + getPlatform();
}
function nativeMouseMoveEventMsg() {
switch (getPlatform()) {
case "windows": return 1; // MOUSEEVENTF_MOVE
case "mac": return 5; // NSMouseMoved
case "linux": return 3; // GDK_MOTION_NOTIFY
case "android": return 7; // ACTION_HOVER_MOVE
}
throw "Native mouse-move events not supported on platform " + getPlatform();
}
function nativeMouseUpEventMsg() {
switch (getPlatform()) {
case "windows": return 4; // MOUSEEVENTF_LEFTUP
case "mac": return 2; // NSLeftMouseUp
case "linux": return 7; // GDK_BUTTON_RELEASE
case "android": return 6; // ACTION_POINTER_UP
}
throw "Native mouse-up events not supported on platform " + getPlatform();
}
// Convert (aX, aY), in CSS pixels relative to aElement's bounding rect,
// to device pixels relative to the screen.
function coordinatesRelativeToScreen(aX, aY, aElement) {
var targetWindow = aElement.ownerDocument.defaultView;
var scale = targetWindow.devicePixelRatio;
var rect = aElement.getBoundingClientRect();
return {
x: (targetWindow.mozInnerScreenX + rect.left + aX) * scale,
y: (targetWindow.mozInnerScreenY + rect.top + aY) * scale
};
}
// Get the bounding box of aElement, and return it in device pixels
// relative to the screen.
function rectRelativeToScreen(aElement) {
var targetWindow = aElement.ownerDocument.defaultView;
var scale = targetWindow.devicePixelRatio;
var rect = aElement.getBoundingClientRect();
return {
x: (targetWindow.mozInnerScreenX + rect.left) * scale,
y: (targetWindow.mozInnerScreenY + rect.top) * scale,
w: (rect.width * scale),
h: (rect.height * scale)
};
}
// Synthesizes a native mousewheel event and returns immediately. This does not
// guarantee anything; you probably want to use one of the other functions below
// which actually wait for results.
// aX and aY are relative to the top-left of |aElement|'s containing window.
// aDeltaX and aDeltaY are pixel deltas, and aObserver can be left undefined
// if not needed.
function synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, aObserver) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
if (aDeltaX && aDeltaY) {
throw "Simultaneous wheeling of horizontal and vertical is not supported on all platforms.";
}
aDeltaX = nativeScrollUnits(aElement, aDeltaX);
aDeltaY = nativeScrollUnits(aElement, aDeltaY);
var msg = aDeltaX ? nativeHorizontalWheelEventMsg() : nativeVerticalWheelEventMsg();
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseScrollEvent(pt.x, pt.y, msg, aDeltaX, aDeltaY, 0, 0, 0, aElement, aObserver);
return true;
}
// Synthesizes a native mousewheel event and invokes the callback once the
// request has been successfully made to the OS. This does not necessarily
// guarantee that the OS generates the event we requested. See
// synthesizeNativeWheel for details on the parameters.
function synthesizeNativeWheelAndWaitForObserver(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
var observer = {
observe: function(aSubject, aTopic, aData) {
if (aCallback && aTopic == "mousescrollevent") {
setTimeout(aCallback, 0);
}
}
};
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY, observer);
}
// Synthesizes a native mousewheel event and invokes the callback once the
// wheel event is dispatched to |aElement|'s containing window. If the event
// targets content in a subdocument, |aElement| should be inside the
// subdocument. See synthesizeNativeWheel for details on the other parameters.
function synthesizeNativeWheelAndWaitForWheelEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
var targetWindow = aElement.ownerDocument.defaultView;
targetWindow.addEventListener("wheel", function wheelWaiter(e) {
targetWindow.removeEventListener("wheel", wheelWaiter);
setTimeout(aCallback, 0);
});
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
}
// Synthesizes a native mousewheel event and invokes the callback once the
// first resulting scroll event is dispatched to |aElement|'s containing window.
// If the event targets content in a subdocument, |aElement| should be inside
// the subdocument. See synthesizeNativeWheel for details on the other
// parameters.
function synthesizeNativeWheelAndWaitForScrollEvent(aElement, aX, aY, aDeltaX, aDeltaY, aCallback) {
var targetWindow = aElement.ownerDocument.defaultView;
var useCapture = true; // scroll events don't always bubble
targetWindow.addEventListener("scroll", function scrollWaiter(e) {
targetWindow.removeEventListener("scroll", scrollWaiter, useCapture);
setTimeout(aCallback, 0);
}, useCapture);
return synthesizeNativeWheel(aElement, aX, aY, aDeltaX, aDeltaY);
}
// Synthesizes a native mouse move event and returns immediately.
// aX and aY are relative to the top-left of |aElement|'s containing window.
function synthesizeNativeMouseMove(aElement, aX, aY) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseMoveEventMsg(), 0, aElement);
return true;
}
// Synthesizes a native mouse move event and invokes the callback once the
// mouse move event is dispatched to |aElement|'s containing window. If the event
// targets content in a subdocument, |aElement| should be inside the
// subdocument. See synthesizeNativeMouseMove for details on the other
// parameters.
function synthesizeNativeMouseMoveAndWaitForMoveEvent(aElement, aX, aY, aCallback) {
var targetWindow = aElement.ownerDocument.defaultView;
targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
targetWindow.removeEventListener("mousemove", mousemoveWaiter);
setTimeout(aCallback, 0);
});
return synthesizeNativeMouseMove(aElement, aX, aY);
}
// Synthesizes a native touch event and dispatches it. aX and aY in CSS pixels
// relative to the top-left of |aElement|'s bounding rect.
function synthesizeNativeTouch(aElement, aX, aY, aType, aObserver = null, aTouchId = 0) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchPoint(aTouchId, aType, pt.x, pt.y, 1, 90, aObserver);
return true;
}
// A handy constant when synthesizing native touch drag events with the pref
// "apz.touch_start_tolerance" set to 0. In this case, the first touchmove with
// a nonzero pixel movement is consumed by the APZ to transition from the
// "touching" state to the "panning" state, so calls to synthesizeNativeTouchDrag
// should add an extra pixel pixel for this purpose. The TOUCH_SLOP provides
// a constant that can be used for this purpose. Note that if the touch start
// tolerance is set to something higher, the touch slop amount used must be
// correspondingly increased so as to be higher than the tolerance.
const TOUCH_SLOP = 1;
function synthesizeNativeTouchDrag(aElement, aX, aY, aDeltaX, aDeltaY, aObserver = null, aTouchId = 0) {
synthesizeNativeTouch(aElement, aX, aY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
var steps = Math.max(Math.abs(aDeltaX), Math.abs(aDeltaY));
for (var i = 1; i < steps; i++) {
var dx = i * (aDeltaX / steps);
var dy = i * (aDeltaY / steps);
synthesizeNativeTouch(aElement, aX + dx, aY + dy, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
}
synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, aTouchId);
return synthesizeNativeTouch(aElement, aX + aDeltaX, aY + aDeltaY, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, aObserver, aTouchId);
}
function synthesizeNativeTap(aElement, aX, aY, aObserver = null) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeTouchTap(pt.x, pt.y, false, aObserver);
return true;
}
function synthesizeNativeMouseEvent(aElement, aX, aY, aType, aObserver = null) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseEvent(pt.x, pt.y, aType, 0, aElement, aObserver);
return true;
}
function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
var pt = coordinatesRelativeToScreen(aX, aY, aElement);
var utils = SpecialPowers.getDOMWindowUtils(aElement.ownerDocument.defaultView);
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseDownEventMsg(), 0, aElement, function() {
utils.sendNativeMouseEvent(pt.x, pt.y, nativeMouseUpEventMsg(), 0, aElement, aObserver);
});
return true;
}
// Move the mouse to (dx, dy) relative to |element|, and scroll the wheel
// at that location.
// Moving the mouse is necessary to avoid wheel events from two consecutive
// moveMouseAndScrollWheelOver() calls on different elements being incorrectly
// considered as part of the same wheel transaction.
// We also wait for the mouse move event to be processed before sending the
// wheel event, otherwise there is a chance they might get reordered, and
// we have the transaction problem again.
function moveMouseAndScrollWheelOver(element, dx, dy, testDriver, waitForScroll = true) {
return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
if (waitForScroll) {
synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);
} else {
synthesizeNativeWheelAndWaitForWheelEvent(element, dx, dy, 0, -10, testDriver);
}
});
}

View file

@ -0,0 +1,403 @@
// Utilities for writing APZ tests using the framework added in bug 961289
// ----------------------------------------------------------------------
// Functions that convert the APZ test data into a more usable form.
// Every place we have a WebIDL sequence whose elements are dictionaries
// with two elements, a key, and a value, we convert this into a JS
// object with a property for each key/value pair. (This is the structure
// we really want, but we can't express in directly in WebIDL.)
// ----------------------------------------------------------------------
function convertEntries(entries) {
var result = {};
for (var i = 0; i < entries.length; ++i) {
result[entries[i].key] = entries[i].value;
}
return result;
}
function getPropertyAsRect(scrollFrames, scrollId, prop) {
SimpleTest.ok(scrollId in scrollFrames,
'expected scroll frame data for scroll id ' + scrollId);
var scrollFrameData = scrollFrames[scrollId];
SimpleTest.ok('displayport' in scrollFrameData,
'expected a ' + prop + ' for scroll id ' + scrollId);
var value = scrollFrameData[prop];
var pieces = value.replace(/[()\s]+/g, '').split(',');
SimpleTest.is(pieces.length, 4, "expected string of form (x,y,w,h)");
return { x: parseInt(pieces[0]),
y: parseInt(pieces[1]),
w: parseInt(pieces[2]),
h: parseInt(pieces[3]) };
}
function convertScrollFrameData(scrollFrames) {
var result = {};
for (var i = 0; i < scrollFrames.length; ++i) {
result[scrollFrames[i].scrollId] = convertEntries(scrollFrames[i].entries);
}
return result;
}
function convertBuckets(buckets) {
var result = {};
for (var i = 0; i < buckets.length; ++i) {
result[buckets[i].sequenceNumber] = convertScrollFrameData(buckets[i].scrollFrames);
}
return result;
}
function convertTestData(testData) {
var result = {};
result.paints = convertBuckets(testData.paints);
result.repaintRequests = convertBuckets(testData.repaintRequests);
return result;
}
// Given APZ test data for a single paint on the compositor side,
// reconstruct the APZC tree structure from the 'parentScrollId'
// entries that were logged. More specifically, the subset of the
// APZC tree structure corresponding to the layer subtree for the
// content process that triggered the paint, is reconstructed (as
// the APZ test data only contains information abot this subtree).
function buildApzcTree(paint) {
// The APZC tree can potentially have multiple root nodes,
// so we invent a node that is the parent of all roots.
// This 'root' does not correspond to an APZC.
var root = {scrollId: -1, children: []};
for (var scrollId in paint) {
paint[scrollId].children = [];
paint[scrollId].scrollId = scrollId;
}
for (var scrollId in paint) {
var parentNode = null;
if ("hasNoParentWithSameLayersId" in paint[scrollId]) {
parentNode = root;
} else if ("parentScrollId" in paint[scrollId]) {
parentNode = paint[paint[scrollId].parentScrollId];
}
parentNode.children.push(paint[scrollId]);
}
return root;
}
// Given an APZC tree produced by buildApzcTree, return the RCD node in
// the tree, or null if there was none.
function findRcdNode(apzcTree) {
if (!!apzcTree.isRootContent) { // isRootContent will be undefined or "1"
return apzcTree;
}
for (var i = 0; i < apzcTree.children.length; i++) {
var rcd = findRcdNode(apzcTree.children[i]);
if (rcd != null) {
return rcd;
}
}
return null;
}
// Return whether an element whose id includes |elementId| has been layerized.
// Assumes |elementId| will be present in the content description for the
// element, and not in the content descriptions of other elements.
function isLayerized(elementId) {
var contentTestData = SpecialPowers.getDOMWindowUtils(window).getContentAPZTestData();
ok(contentTestData.paints.length > 0, "expected at least one paint");
var seqno = contentTestData.paints[contentTestData.paints.length - 1].sequenceNumber;
contentTestData = convertTestData(contentTestData);
var paint = contentTestData.paints[seqno];
for (var scrollId in paint) {
if ("contentDescription" in paint[scrollId]) {
if (paint[scrollId]["contentDescription"].includes(elementId)) {
return true;
}
}
}
return false;
}
function flushApzRepaints(aCallback, aWindow = window) {
if (!aCallback) {
throw "A callback must be provided!";
}
var repaintDone = function() {
SpecialPowers.Services.obs.removeObserver(repaintDone, "apz-repaints-flushed", false);
setTimeout(aCallback, 0);
};
SpecialPowers.Services.obs.addObserver(repaintDone, "apz-repaints-flushed", false);
if (SpecialPowers.getDOMWindowUtils(aWindow).flushApzRepaints()) {
dump("Flushed APZ repaints, waiting for callback...\n");
} else {
dump("Flushing APZ repaints was a no-op, triggering callback directly...\n");
repaintDone();
}
}
// Flush repaints, APZ pending repaints, and any repaints resulting from that
// flush. This is particularly useful if the test needs to reach some sort of
// "idle" state in terms of repaints. Usually just doing waitForAllPaints
// followed by flushApzRepaints is sufficient to flush all APZ state back to
// the main thread, but it can leave a paint scheduled which will get triggered
// at some later time. For tests that specifically test for painting at
// specific times, this method is the way to go. Even if in doubt, this is the
// preferred method as the extra step is "safe" and shouldn't interfere with
// most tests.
function waitForApzFlushedRepaints(aCallback) {
// First flush the main-thread paints and send transactions to the APZ
waitForAllPaints(function() {
// Then flush the APZ to make sure any repaint requests have been sent
// back to the main thread
flushApzRepaints(function() {
// Then flush the main-thread again to process the repaint requests.
// Once this is done, we should be in a stable state with nothing
// pending, so we can trigger the callback.
waitForAllPaints(aCallback);
});
});
}
// This function takes a set of subtests to run one at a time in new top-level
// windows, and returns a Promise that is resolved once all the subtests are
// done running.
//
// The aSubtests array is an array of objects with the following keys:
// file: required, the filename of the subtest.
// prefs: optional, an array of arrays containing key-value prefs to set.
// dp_suppression: optional, a boolean on whether or not to respect displayport
// suppression during the test.
// onload: optional, a function that will be registered as a load event listener
// for the child window that will hold the subtest. the function will be
// passed exactly one argument, which will be the child window.
// An example of an array is:
// aSubtests = [
// { 'file': 'test_file_name.html' },
// { 'file': 'test_file_2.html', 'prefs': [['pref.name', true], ['other.pref', 1000]], 'dp_suppression': false }
// { 'file': 'file_3.html', 'onload': function(w) { w.subtestDone(); } }
// ];
//
// Each subtest should call the subtestDone() function when it is done, to
// indicate that the window should be torn down and the next text should run.
// The subtestDone() function is injected into the subtest's window by this
// function prior to loading the subtest. For convenience, the |is| and |ok|
// functions provided by SimpleTest are also mapped into the subtest's window.
// For other things from the parent, the subtest can use window.opener.<whatever>
// to access objects.
function runSubtestsSeriallyInFreshWindows(aSubtests) {
return new Promise(function(resolve, reject) {
var testIndex = -1;
var w = null;
function advanceSubtestExecution() {
var test = aSubtests[testIndex];
if (w) {
if (typeof test.dp_suppression != 'undefined') {
// We modified the suppression when starting the test, so now undo that.
SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(!test.dp_suppression);
}
if (test.prefs) {
// We pushed some prefs for this test, pop them, and re-invoke
// advanceSubtestExecution() after that's been processed
SpecialPowers.popPrefEnv(function() {
w.close();
w = null;
advanceSubtestExecution();
});
return;
}
w.close();
}
testIndex++;
if (testIndex >= aSubtests.length) {
resolve();
return;
}
test = aSubtests[testIndex];
if (typeof test.dp_suppression != 'undefined') {
// Normally during a test, the displayport will get suppressed during page
// load, and unsuppressed at a non-deterministic time during the test. The
// unsuppression can trigger a repaint which interferes with the test, so
// to avoid that we can force the displayport to be unsuppressed for the
// entire test which is more deterministic.
SpecialPowers.getDOMWindowUtils(window).respectDisplayPortSuppression(test.dp_suppression);
}
function spawnTest(aFile) {
w = window.open('', "_blank");
w.subtestDone = advanceSubtestExecution;
w.SimpleTest = SimpleTest;
w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
if (test.onload) {
w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
}
w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
return w;
}
if (test.prefs) {
// Got some prefs for this subtest, push them
SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
w = spawnTest(test.file);
});
} else {
w = spawnTest(test.file);
}
}
advanceSubtestExecution();
});
}
function pushPrefs(prefs) {
return SpecialPowers.pushPrefEnv({'set': prefs});
}
function waitUntilApzStable() {
return new Promise(function(resolve, reject) {
SimpleTest.waitForFocus(function() {
waitForAllPaints(function() {
flushApzRepaints(resolve);
});
}, window);
});
}
function isApzEnabled() {
var enabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
if (!enabled) {
// All tests are required to have at least one assertion. Since APZ is
// disabled, and the main test is presumably not going to run, we stick in
// a dummy assertion here to keep the test passing.
SimpleTest.ok(true, "APZ is not enabled; this test will be skipped");
}
return enabled;
}
// Despite what this function name says, this does not *directly* run the
// provided continuation testFunction. Instead, it returns a function that
// can be used to run the continuation. The extra level of indirection allows
// it to be more easily added to a promise chain, like so:
// waitUntilApzStable().then(runContinuation(myTest));
//
// If you want to run the continuation directly, outside of a promise chain,
// you can invoke the return value of this function, like so:
// runContinuation(myTest)();
function runContinuation(testFunction) {
// We need to wrap this in an extra function, so that the call site can
// be more readable without running the promise too early. In other words,
// if we didn't have this extra function, the promise would start running
// during construction of the promise chain, concurrently with the first
// promise in the chain.
return function() {
return new Promise(function(resolve, reject) {
var testContinuation = null;
function driveTest() {
if (!testContinuation) {
testContinuation = testFunction(driveTest);
}
var ret = testContinuation.next();
if (ret.done) {
resolve();
}
}
driveTest();
});
};
}
// Take a snapshot of the given rect, *including compositor transforms* (i.e.
// includes async scroll transforms applied by APZ). If you don't need the
// compositor transforms, you can probably get away with using
// SpecialPowers.snapshotWindowWithOptions or one of the friendlier wrappers.
// The rect provided is expected to be relative to the screen, for example as
// returned by rectRelativeToScreen in apz_test_native_event_utils.js.
// Example usage:
// var snapshot = getSnapshot(rectRelativeToScreen(myDiv));
// which will take a snapshot of the 'myDiv' element. Note that if part of the
// element is obscured by other things on top, the snapshot will include those
// things. If it is clipped by a scroll container, the snapshot will include
// that area anyway, so you will probably get parts of the scroll container in
// the snapshot. If the rect extends outside the browser window then the
// results are undefined.
// The snapshot is returned in the form of a data URL.
function getSnapshot(rect) {
function parentProcessSnapshot() {
addMessageListener('snapshot', function(rect) {
Components.utils.import('resource://gre/modules/Services.jsm');
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
// reposition the rect relative to the top-level browser window
rect = JSON.parse(rect);
rect.x -= topWin.mozInnerScreenX;
rect.y -= topWin.mozInnerScreenY;
// take the snapshot
var canvas = topWin.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.width = rect.w;
canvas.height = rect.h;
var ctx = canvas.getContext("2d");
ctx.drawWindow(topWin, rect.x, rect.y, rect.w, rect.h, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_CARET);
return canvas.toDataURL();
});
}
if (typeof getSnapshot.chromeHelper == 'undefined') {
// This is the first time getSnapshot is being called; do initialization
getSnapshot.chromeHelper = SpecialPowers.loadChromeScript(parentProcessSnapshot);
SimpleTest.registerCleanupFunction(function() { getSnapshot.chromeHelper.destroy() });
}
return getSnapshot.chromeHelper.sendSyncMessage('snapshot', JSON.stringify(rect)).toString();
}
// Takes the document's query string and parses it, assuming the query string
// is composed of key-value pairs where the value is in JSON format. The object
// returned contains the various values indexed by their respective keys. In
// case of duplicate keys, the last value be used.
// Examples:
// ?key="value"&key2=false&key3=500
// produces { "key": "value", "key2": false, "key3": 500 }
// ?key={"x":0,"y":50}&key2=[1,2,true]
// produces { "key": { "x": 0, "y": 0 }, "key2": [1, 2, true] }
function getQueryArgs() {
var args = {};
if (location.search.length > 0) {
var params = location.search.substr(1).split('&');
for (var p of params) {
var [k, v] = p.split('=');
args[k] = JSON.parse(v);
}
}
return args;
}
// Return a function that returns a promise to create a script element with the
// given URI and append it to the head of the document in the given window.
// As with runContinuation(), the extra function wrapper is for convenience
// at the call site, so that this can be chained with other promises:
// waitUntilApzStable().then(injectScript('foo'))
// .then(injectScript('bar'));
// If you want to do the injection right away, run the function returned by
// this function:
// injectScript('foo')();
function injectScript(aScript, aWindow = window) {
return function() {
return new Promise(function(resolve, reject) {
var e = aWindow.document.createElement('script');
e.type = 'text/javascript';
e.onload = function() {
resolve();
};
e.onerror = function() {
dump('Script [' + aScript + '] errored out\n');
reject();
};
e.src = aScript;
aWindow.document.getElementsByTagName('head')[0].appendChild(e);
});
};
}

View file

@ -0,0 +1,9 @@
[DEFAULT]
support-files =
apz_test_native_event_utils.js
tags = apz-chrome
[test_smoothness.html]
# hardware vsync only on win/mac
# e10s only since APZ is only enabled on e10s
skip-if = debug || (os != 'mac' && os != 'win') || !e10s

View file

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity panning test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function scrollPage() {
var transformEnd = function() {
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll);
};
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
synthesizeNativeTouchDrag(document.body, 10, 100, 0, -(50 + TOUCH_SLOP));
dump("Finished native drag, waiting for transform-end observer...\n");
}
function checkScroll() {
is(window.scrollY, 50, "check that the window scrolled");
subtestDone();
}
waitUntilApzStable().then(scrollPage);
</script>
</head>
<body>
<div style="height: 5000px; background-color: lightgreen;">
This div makes the page scrollable.
</div>
</body>
</html>

View file

@ -0,0 +1,83 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1151663, helper page</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript">
// -------------------------------------------------------------------
// Infrastructure to get the test assertions to run at the right time.
// -------------------------------------------------------------------
var SimpleTest = window.opener.SimpleTest;
window.onload = function() {
window.addEventListener("MozAfterPaint", afterPaint, false);
};
var utils = SpecialPowers.getDOMWindowUtils(window);
function afterPaint(e) {
// If there is another paint pending, wait for it.
if (utils.isMozAfterPaintPending) {
return;
}
// Once there are no more paints pending, remove the
// MozAfterPaint listener and run the test logic.
window.removeEventListener("MozAfterPaint", afterPaint, false);
testBug1151663();
}
// --------------------------------------------------------------------
// The actual logic for testing bug 1151663.
//
// In this test we have a simple page which is scrollable, with a
// scrollable <div> which is also scrollable. We test that the
// <div> does not get an initial APZC, since primary scrollable
// frame is the page's root scroll frame.
// --------------------------------------------------------------------
function testBug1151663() {
// Get the content- and compositor-side test data from nsIDOMWindowUtils.
var contentTestData = utils.getContentAPZTestData();
var compositorTestData = utils.getCompositorAPZTestData();
// Get the sequence number of the last paint on the compositor side.
// We do this before converting the APZ test data because the conversion
// loses the order of the paints.
SimpleTest.ok(compositorTestData.paints.length > 0,
"expected at least one paint in compositor test data");
var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
// Convert the test data into a representation that's easier to navigate.
contentTestData = convertTestData(contentTestData);
compositorTestData = convertTestData(compositorTestData);
var paint = compositorTestData.paints[lastCompositorPaintSeqNo];
// Reconstruct the APZC tree structure in the last paint.
var apzcTree = buildApzcTree(paint);
// The apzc tree for this page should consist of a single root APZC,
// which either is the RCD with no child APZCs (e10s/B2G case) or has a
// single child APZC which is for the RCD (fennec case).
var rcd = findRcdNode(apzcTree);
SimpleTest.ok(rcd != null, "found the RCD node");
SimpleTest.is(rcd.children.length, 0, "expected no children on the RCD");
window.opener.finishTest();
}
</script>
</head>
<body style="height: 500px; overflow: scroll">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
<div style="height: 50px; width: 50px; overflow: scroll">
<!-- Put enough content into the subframe to make it have a nonzero scroll range -->
<div style="height: 100px; width: 50px"></div>
</div>
<!-- Put enough content into the page to make it have a nonzero scroll range -->
<div style="height: 1000px"></div>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Test for touchend on media elements</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var v = document.getElementById('video');
var a = document.getElementById('audio');
var d = document.getElementById('div');
document.body.ontouchstart = function(e) {
if (e.target === v || e.target === a || e.target === d) {
e.target.style.display = 'none';
ok(true, 'Set display to none on #' + e.target.id);
} else {
ok(false, 'Got unexpected touchstart on ' + e.target);
}
waitForAllPaints(testDriver);
};
document.body.ontouchend = function(e) {
if (e.target === v || e.target === a || e.target === d) {
e.target._gotTouchend = true;
ok(true, 'Got touchend event on #' + e.target.id);
}
testDriver();
};
var utils = SpecialPowers.getDOMWindowUtils(window);
var pt = coordinatesRelativeToScreen(25, 5, v);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
ok(v._gotTouchend, 'Touchend was received on video element');
pt = coordinatesRelativeToScreen(25, 5, a);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
ok(a._gotTouchend, 'Touchend was received on audio element');
pt = coordinatesRelativeToScreen(25, 5, d);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, pt.x, pt.y, 1, 90, null);
yield utils.sendNativeTouchPoint(0, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, pt.x, pt.y, 1, 90, null);
ok(d._gotTouchend, 'Touchend was received on div element');
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
<style>
* {
font-size: 24px;
box-sizing: border-box;
}
#video {
display:block;
position:absolute;
top: 100px;
left:0;
width: 33%;
height: 100px;
border:solid black 1px;
background-color: #8a8;
}
#audio {
display:block;
position:absolute;
top: 100px;
left:33%;
width: 33%;
height: 100px;
border:solid black 1px;
background-color: #a88;
}
#div {
display:block;
position:absolute;
top: 100px;
left: 66%;
width: 34%;
height: 100px;
border:solid black 1px;
background-color: #88a;
}
</style>
</head>
<body>
<p>Tap on the colored boxes to hide them.</p>
<video id="video"></video>
<audio id="audio" controls></audio>
<div id="div"></div>
</body>
</html>

View file

@ -0,0 +1,574 @@
<head>
<title>Ensure that the hit region doesn't get unexpectedly expanded</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var scroller = document.getElementById('scroller');
var scrollerPos = scroller.scrollTop;
var dx = 100, dy = 50;
is(window.scrollY, 0, "Initial page scroll position should be 0");
is(scrollerPos, 0, "Initial scroller position should be 0");
yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
is(window.scrollY, 0, "Page scroll position should still be 0");
ok(scroller.scrollTop > scrollerPos, "Scroller should have scrolled");
// wait for it to layerize fully and then try again
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
scrollerPos = scroller.scrollTop;
yield moveMouseAndScrollWheelOver(scroller, dx, dy, testDriver);
is(window.scrollY, 0, "Page scroll position should still be 0 after layerization");
ok(scroller.scrollTop > scrollerPos, "Scroller should have continued scrolling");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
<style>
a#with_after_content {
background-color: #F16725;
opacity: 0.8;
display: inline-block;
margin-top: 40px;
margin-left: 40px;
}
a#with_after_content::after {
content: " ";
position: absolute;
width: 0px;
height: 0px;
bottom: 40px;
z-index: -1;
right: 40px;
background-color: transparent;
border-style: solid;
border-width: 15px 15px 15px 0;
border-color: #d54e0e transparent transparent transparent;
box-shadow: none;
box-sizing: border-box;
}
div#scroller {
overflow-y: scroll;
width: 50%;
height: 50%;
}
</style>
</head>
<body>
<a id="with_after_content">Some text</a>
<div id="scroller">
Scrolling on the very left edge of this div will work.
Scrolling on the right side of this div (starting with the left edge of the orange box above) should work, but doesn't.<br/>
0<br>
1<br>
2<br>
3<br>
4<br>
5<br>
6<br>
7<br>
8<br>
9<br>
10<br>
11<br>
12<br>
13<br>
14<br>
15<br>
16<br>
17<br>
18<br>
19<br>
20<br>
21<br>
22<br>
23<br>
24<br>
25<br>
26<br>
27<br>
28<br>
29<br>
30<br>
31<br>
32<br>
33<br>
34<br>
35<br>
36<br>
37<br>
38<br>
39<br>
40<br>
41<br>
42<br>
43<br>
44<br>
45<br>
46<br>
47<br>
48<br>
49<br>
50<br>
51<br>
52<br>
53<br>
54<br>
55<br>
56<br>
57<br>
58<br>
59<br>
60<br>
61<br>
62<br>
63<br>
64<br>
65<br>
66<br>
67<br>
68<br>
69<br>
70<br>
71<br>
72<br>
73<br>
74<br>
75<br>
76<br>
77<br>
78<br>
79<br>
80<br>
81<br>
82<br>
83<br>
84<br>
85<br>
86<br>
87<br>
88<br>
89<br>
90<br>
91<br>
92<br>
93<br>
94<br>
95<br>
96<br>
97<br>
98<br>
99<br>
100<br>
101<br>
102<br>
103<br>
104<br>
105<br>
106<br>
107<br>
108<br>
109<br>
110<br>
111<br>
112<br>
113<br>
114<br>
115<br>
116<br>
117<br>
118<br>
119<br>
120<br>
121<br>
122<br>
123<br>
124<br>
125<br>
126<br>
127<br>
128<br>
129<br>
130<br>
131<br>
132<br>
133<br>
134<br>
135<br>
136<br>
137<br>
138<br>
139<br>
140<br>
141<br>
142<br>
143<br>
144<br>
145<br>
146<br>
147<br>
148<br>
149<br>
150<br>
151<br>
152<br>
153<br>
154<br>
155<br>
156<br>
157<br>
158<br>
159<br>
160<br>
161<br>
162<br>
163<br>
164<br>
165<br>
166<br>
167<br>
168<br>
169<br>
170<br>
171<br>
172<br>
173<br>
174<br>
175<br>
176<br>
177<br>
178<br>
179<br>
180<br>
181<br>
182<br>
183<br>
184<br>
185<br>
186<br>
187<br>
188<br>
189<br>
190<br>
191<br>
192<br>
193<br>
194<br>
195<br>
196<br>
197<br>
198<br>
199<br>
200<br>
201<br>
202<br>
203<br>
204<br>
205<br>
206<br>
207<br>
208<br>
209<br>
210<br>
211<br>
212<br>
213<br>
214<br>
215<br>
216<br>
217<br>
218<br>
219<br>
220<br>
221<br>
222<br>
223<br>
224<br>
225<br>
226<br>
227<br>
228<br>
229<br>
230<br>
231<br>
232<br>
233<br>
234<br>
235<br>
236<br>
237<br>
238<br>
239<br>
240<br>
241<br>
242<br>
243<br>
244<br>
245<br>
246<br>
247<br>
248<br>
249<br>
250<br>
251<br>
252<br>
253<br>
254<br>
255<br>
256<br>
257<br>
258<br>
259<br>
260<br>
261<br>
262<br>
263<br>
264<br>
265<br>
266<br>
267<br>
268<br>
269<br>
270<br>
271<br>
272<br>
273<br>
274<br>
275<br>
276<br>
277<br>
278<br>
279<br>
280<br>
281<br>
282<br>
283<br>
284<br>
285<br>
286<br>
287<br>
288<br>
289<br>
290<br>
291<br>
292<br>
293<br>
294<br>
295<br>
296<br>
297<br>
298<br>
299<br>
300<br>
301<br>
302<br>
303<br>
304<br>
305<br>
306<br>
307<br>
308<br>
309<br>
310<br>
311<br>
312<br>
313<br>
314<br>
315<br>
316<br>
317<br>
318<br>
319<br>
320<br>
321<br>
322<br>
323<br>
324<br>
325<br>
326<br>
327<br>
328<br>
329<br>
330<br>
331<br>
332<br>
333<br>
334<br>
335<br>
336<br>
337<br>
338<br>
339<br>
340<br>
341<br>
342<br>
343<br>
344<br>
345<br>
346<br>
347<br>
348<br>
349<br>
350<br>
351<br>
352<br>
353<br>
354<br>
355<br>
356<br>
357<br>
358<br>
359<br>
360<br>
361<br>
362<br>
363<br>
364<br>
365<br>
366<br>
367<br>
368<br>
369<br>
370<br>
371<br>
372<br>
373<br>
374<br>
375<br>
376<br>
377<br>
378<br>
379<br>
380<br>
381<br>
382<br>
383<br>
384<br>
385<br>
386<br>
387<br>
388<br>
389<br>
390<br>
391<br>
392<br>
393<br>
394<br>
395<br>
396<br>
397<br>
398<br>
399<br>
400<br>
401<br>
402<br>
403<br>
404<br>
405<br>
406<br>
407<br>
408<br>
409<br>
410<br>
411<br>
412<br>
413<br>
414<br>
415<br>
416<br>
417<br>
418<br>
419<br>
420<br>
421<br>
422<br>
423<br>
424<br>
425<br>
426<br>
427<br>
428<br>
429<br>
430<br>
431<br>
432<br>
433<br>
434<br>
435<br>
436<br>
437<br>
438<br>
439<br>
440<br>
441<br>
442<br>
443<br>
444<br>
445<br>
446<br>
447<br>
448<br>
449<br>
450<br>
451<br>
452<br>
453<br>
454<br>
455<br>
456<br>
457<br>
458<br>
459<br>
460<br>
461<br>
462<br>
463<br>
464<br>
465<br>
466<br>
467<br>
468<br>
469<br>
470<br>
471<br>
472<br>
473<br>
474<br>
475<br>
476<br>
477<br>
478<br>
479<br>
480<br>
481<br>
482<br>
483<br>
484<br>
485<br>
486<br>
487<br>
488<br>
489<br>
490<br>
491<br>
492<br>
493<br>
494<br>
495<br>
496<br>
497<br>
498<br>
499<br>
</div>
<div style="height: 1000px">this div makes the page scrollable</div>
</body>

View file

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html style="overflow:hidden">
<head>
<meta charset="utf-8">
<!-- The viewport tag will result in APZ being in a "zoomed-in" state, assuming
the device width is less than 980px. -->
<meta name="viewport" content="width=980; initial-scale=1.0">
<title>Test for bug 1280013</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
SimpleTest.ok(screen.height > 500, "Screen height must be at least 500 pixels for this test to work");
// This listener will trigger the test to continue once APZ is done with
// processing the scroll.
SpecialPowers.Services.obs.addObserver(testDriver, "APZ:TransformEnd", false);
// Scroll down to the iframe. Do it in two drags instead of one in case the
// device screen is short
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
yield synthesizeNativeTouchDrag(document.body, 10, 400, 0, -(350 + TOUCH_SLOP));
// Now the top of the visible area should be at y=700 of the top-level page,
// so if the screen is >= 500px tall, the entire iframe should be visible, at
// least vertically.
// However, because of the overflow:hidden on the root elements, all this
// scrolling is happening in APZ and is not reflected in the main-thread
// scroll position (it is stored in the callback transform instead). We check
// this by checking the scroll offset.
yield flushApzRepaints(testDriver);
SimpleTest.is(window.scrollY, 0, "Main-thread scroll position is still at 0");
// Scroll the iframe by 300px. Note that since the main-thread scroll position
// is still 0, the subframe's getBoundingClientRect is going to be off by
// 700 pixels, so we compensate for that here.
var subframe = document.getElementById('subframe');
yield synthesizeNativeTouchDrag(subframe, 10, 200 - 700, 0, -(300 + TOUCH_SLOP));
// Remove the observer, we don't need it any more.
SpecialPowers.Services.obs.removeObserver(testDriver, "APZ:TransformEnd", false);
// Flush any pending paints
yield flushApzRepaints(testDriver);
// get the displayport for the subframe
var utils = SpecialPowers.getDOMWindowUtils(window);
var contentPaints = utils.getContentAPZTestData().paints;
var lastPaint = convertScrollFrameData(contentPaints[contentPaints.length - 1].scrollFrames);
var foundIt = 0;
for (var scrollId in lastPaint) {
if (('contentDescription' in lastPaint[scrollId]) &&
(lastPaint[scrollId]['contentDescription'].includes('tall_html'))) {
var dp = getPropertyAsRect(lastPaint, scrollId, 'criticalDisplayport');
SimpleTest.ok(dp.y <= 0, 'The critical displayport top should be less than or equal to zero to cover the visible part of the subframe; it is ' + dp.y);
SimpleTest.ok(dp.y + dp.h >= subframe.clientHeight, 'The critical displayport bottom should be greater than the clientHeight; it is ' + (dp.y + dp.h));
foundIt++;
}
}
SimpleTest.is(foundIt, 1, "Found exactly one critical displayport for the subframe we were interested in.");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>
<body style="overflow:hidden">
The iframe below is at (0, 800). Scroll it into view, and then scroll the contents. The content should be fully rendered in high-resolution.
<iframe id="subframe" style="position:absolute; left: 0px; top: 800px; width: 600px; height: 350px" src="helper_tall.html"></iframe>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Test pointer events are dispatched once for touch tap</title>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript">
function test() {
let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
"pointerup", "pointerleave", "pointerout"];
let pointerEventsCount = {};
pointerEventsList.forEach((eventName) => {
pointerEventsCount[eventName] = 0;
document.getElementById('div1').addEventListener(eventName, (event) => {
dump("Received event " + event.type + "\n");
++pointerEventsCount[event.type];
}, false);
});
document.addEventListener("click", (event) => {
is(event.target, document.getElementById('div1'), "Clicked on div (at " + event.clientX + "," + event.clientY + ")");
for (var key in pointerEventsCount) {
is(pointerEventsCount[key], 1, "Event " + key + " should be generated once");
}
subtestDone();
}, false);
synthesizeNativeTap(document.getElementById('div1'), 100, 100, () => {
dump("Finished synthesizing tap, waiting for div to be clicked...\n");
});
}
waitUntilApzStable().then(test);
</script>
</head>
<body>
<div id="div1" style="width: 200px; height: 200px; background: black"></div>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Test pointer events are dispatched once for touch tap</title>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript">
/** Test for Bug 1299195 **/
function runTests() {
let target0 = document.getElementById("target0");
let mouseup_count = 0;
let mousedown_count = 0;
let pointerup_count = 0;
let pointerdown_count = 0;
target0.addEventListener("mouseup", () => {
++mouseup_count;
if (mouseup_count == 2) {
is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
subtestDone();
}
});
target0.addEventListener("mousedown", () => {
++mousedown_count;
});
target0.addEventListener("pointerup", () => {
++pointerup_count;
});
target0.addEventListener("pointerdown", () => {
++pointerdown_count;
});
synthesizeNativeTap(document.getElementById('target0'), 100, 100);
synthesizeNativeTap(document.getElementById('target0'), 100, 100);
}
waitUntilApzStable().then(runTests);
</script>
</head>
<body>
<div id="target0" style="width: 200px; height: 200px; background: green"></div>
</body>
</html>

View file

@ -0,0 +1,149 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=982141
-->
<head>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=no">
<title>Test for Bug 982141, helper page</title>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript">
// -------------------------------------------------------------------
// Infrastructure to get the test assertions to run at the right time.
// -------------------------------------------------------------------
var SimpleTest = window.opener.SimpleTest;
window.onload = function() {
window.addEventListener("MozAfterPaint", afterPaint, false);
};
var utils = SpecialPowers.getDOMWindowUtils(window);
function afterPaint(e) {
// If there is another paint pending, wait for it.
if (utils.isMozAfterPaintPending) {
return;
}
// Once there are no more paints pending, remove the
// MozAfterPaint listener and run the test logic.
window.removeEventListener("MozAfterPaint", afterPaint, false);
testBug982141();
}
// --------------------------------------------------------------------
// The actual logic for testing bug 982141.
//
// In this test we have a simple page with a scrollable <div> which has
// enough content to make it scrollable. We test that this <div> got
// a displayport.
// --------------------------------------------------------------------
function testBug982141() {
// Get the content- and compositor-side test data from nsIDOMWindowUtils.
var contentTestData = utils.getContentAPZTestData();
var compositorTestData = utils.getCompositorAPZTestData();
// Get the sequence number of the last paint on the compositor side.
// We do this before converting the APZ test data because the conversion
// loses the order of the paints.
SimpleTest.ok(compositorTestData.paints.length > 0,
"expected at least one paint in compositor test data");
var lastCompositorPaint = compositorTestData.paints[compositorTestData.paints.length - 1];
var lastCompositorPaintSeqNo = lastCompositorPaint.sequenceNumber;
// Convert the test data into a representation that's easier to navigate.
contentTestData = convertTestData(contentTestData);
compositorTestData = convertTestData(compositorTestData);
// Reconstruct the APZC tree structure in the last paint.
var apzcTree = buildApzcTree(compositorTestData.paints[lastCompositorPaintSeqNo]);
// The apzc tree for this page should consist of a single child APZC on
// the RCD node (the child is for scrollable <div>). Note that in e10s/B2G
// cases the RCD will be the root of the tree but on Fennec it will not.
var rcd = findRcdNode(apzcTree);
SimpleTest.ok(rcd != null, "found the RCD node");
SimpleTest.is(rcd.children.length, 1, "expected a single child APZC");
var childScrollId = rcd.children[0].scrollId;
// We should have content-side data for the same paint.
SimpleTest.ok(lastCompositorPaintSeqNo in contentTestData.paints,
"expected a content paint with sequence number" + lastCompositorPaintSeqNo);
var correspondingContentPaint = contentTestData.paints[lastCompositorPaintSeqNo];
var dp = getPropertyAsRect(correspondingContentPaint, childScrollId, 'displayport');
var subframe = document.getElementById('subframe');
// The clientWidth and clientHeight may be less than 50 if there are scrollbars showing.
// In general they will be (50 - <scrollbarwidth>, 50 - <scrollbarheight>).
SimpleTest.ok(subframe.clientWidth > 0, "Expected a non-zero clientWidth, got: " + subframe.clientWidth);
SimpleTest.ok(subframe.clientHeight > 0, "Expected a non-zero clientHeight, got: " + subframe.clientHeight);
SimpleTest.ok(dp.w >= subframe.clientWidth && dp.h >= subframe.clientHeight,
"expected a displayport at least as large as the scrollable element, got " + JSON.stringify(dp));
window.opener.finishTest();
}
</script>
</head>
<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
<!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
<div id="subframe" style="height: 50px; width: 50px; overflow: scroll">
<div style="width: 100px">
Wide content so that the vertical scrollbar for the parent div
doesn't eat into the 50px width and reduce the width of the
displayport.
</div>
Line 1<br>
Line 2<br>
Line 3<br>
Line 4<br>
Line 5<br>
Line 6<br>
Line 7<br>
Line 8<br>
Line 9<br>
Line 10<br>
Line 11<br>
Line 12<br>
Line 13<br>
Line 14<br>
Line 15<br>
Line 16<br>
Line 17<br>
Line 18<br>
Line 19<br>
Line 20<br>
Line 21<br>
Line 22<br>
Line 23<br>
Line 24<br>
Line 25<br>
Line 26<br>
Line 27<br>
Line 28<br>
Line 29<br>
Line 30<br>
Line 31<br>
Line 32<br>
Line 33<br>
Line 34<br>
Line 35<br>
Line 36<br>
Line 37<br>
Line 38<br>
Line 39<br>
Line 40<br>
Line 41<br>
Line 42<br>
Line 43<br>
Line 44<br>
Line 45<br>
Line 46<br>
Line 40<br>
Line 48<br>
Line 49<br>
Line 50<br>
</div>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity mouse-clicking test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* clickButton(testDriver) {
document.addEventListener('click', clicked, false);
if (getQueryArgs()['dtc']) {
// force a dispatch-to-content region on the document
document.addEventListener('wheel', function() { /* no-op */ }, { passive: false });
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
}
synthesizeNativeClick(document.getElementById('b'), 5, 5, function() {
dump("Finished synthesizing click, waiting for button to be clicked...\n");
});
}
function clicked(e) {
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
subtestDone();
}
waitUntilApzStable()
.then(runContinuation(clickButton));
</script>
</head>
<body>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity panning test for scrollable div</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function scrollOuter() {
var transformEnd = function() {
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll);
};
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
synthesizeNativeTouchDrag(document.getElementById('outer'), 10, 100, 0, -(50 + TOUCH_SLOP));
dump("Finished native drag, waiting for transform-end observer...\n");
}
function checkScroll() {
var outerScroll = document.getElementById('outer').scrollTop;
is(outerScroll, 50, "check that the div scrolled");
subtestDone();
}
waitUntilApzStable().then(scrollOuter);
</script>
</head>
<body>
<div id="outer" style="height: 250px; border: solid 1px black; overflow:scroll">
<div style="height: 5000px; background-color: lightblue">
This div makes the |outer| div scrollable.
</div>
</div>
<div style="height: 5000px; background-color: lightgreen;">
This div makes the top-level page scrollable.
</div>
</body>
</html>

View file

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity mouse-drag click test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
document.addEventListener('click', clicked, false);
// Ensure the pointer is inside the window
yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseMoveEventMsg(), testDriver);
// mouse down, move it around, and release it near where it went down. this
// should generate a click at the release point
yield synthesizeNativeMouseEvent(document.getElementById('b'), 5, 5, nativeMouseDownEventMsg(), testDriver);
yield synthesizeNativeMouseEvent(document.getElementById('b'), 100, 100, nativeMouseMoveEventMsg(), testDriver);
yield synthesizeNativeMouseEvent(document.getElementById('b'), 10, 10, nativeMouseMoveEventMsg(), testDriver);
yield synthesizeNativeMouseEvent(document.getElementById('b'), 8, 8, nativeMouseUpEventMsg(), testDriver);
dump("Finished synthesizing click with a drag in the middle\n");
}
function clicked(e) {
// The mouse down at (5, 5) should not have generated a click, but the up
// at (8, 8) should have.
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
is(e.clientX, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().left), 'x-coord of click event looks sane');
is(e.clientY, 8 + Math.floor(document.getElementById('b').getBoundingClientRect().top), 'y-coord of click event looks sane');
subtestDone();
}
waitUntilApzStable()
.then(runContinuation(test));
</script>
</head>
<body>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>

View file

@ -0,0 +1,603 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Dragging the mouse on a content-implemented scrollbar</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<style>
body {
background: linear-gradient(135deg, red, blue);
}
#scrollbar {
position:fixed;
top: 0;
right: 10px;
height: 100%;
width: 150px;
background-color: gray;
}
</style>
<script type="text/javascript">
var bar = null;
var mouseDown = false;
function moveTo(mouseY, testDriver) {
var fraction = (mouseY - bar.getBoundingClientRect().top) / bar.getBoundingClientRect().height;
fraction = Math.max(0, fraction);
fraction = Math.min(1, fraction);
var oldScrollPos = document.scrollingElement.scrollTop;
var newScrollPos = fraction * window.scrollMaxY;
SimpleTest.ok(newScrollPos > oldScrollPos, "Scroll position strictly increased");
// split the scroll in two with a paint in between, just to increase the
// complexity of the simulated web content, and to ensure this works as well.
document.scrollingElement.scrollTop = (oldScrollPos + newScrollPos) / 2;
waitForAllPaints(function() {
document.scrollingElement.scrollTop = newScrollPos;
testDriver();
});
}
function setupDragging(testDriver) {
bar = document.getElementById('scrollbar');
mouseDown = false;
bar.addEventListener('mousedown', function(e) {
mouseDown = true;
moveTo(e.clientY, testDriver);
}, true);
bar.addEventListener('mousemove', function(e) {
if (mouseDown) {
dump("Got mousemove clientY " + e.clientY + "\n");
moveTo(e.clientY, testDriver);
e.stopPropagation();
}
}, true);
bar.addEventListener('mouseup', function(e) {
mouseDown = false;
}, true);
window.addEventListener('mousemove', function(e) {
if (mouseDown) {
SimpleTest.ok(false, "The mousemove at " + e.clientY + " was not stopped by the bar listener, and is a glitchy event!");
setTimeout(testDriver, 0);
}
}, false);
}
function* test(testDriver) {
setupDragging(testDriver);
// Move the mouse to the "scrollbar" (the div upon which dragging changes scroll position)
yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseMoveEventMsg(), testDriver);
// mouse down
yield synthesizeNativeMouseEvent(bar, 10, 10, nativeMouseDownEventMsg());
// drag vertically by 400px, in 50px increments
yield synthesizeNativeMouseEvent(bar, 10, 60, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 110, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 160, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 210, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 260, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 310, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 360, nativeMouseMoveEventMsg());
yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseMoveEventMsg());
// and release
yield synthesizeNativeMouseEvent(bar, 10, 410, nativeMouseUpEventMsg(), testDriver);
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>
<body>
<div id="scrollbar">Drag up and down on this bar. The background/scrollbar shouldn't glitch</div>
This is a tall page<br/>
1<br/>
2<br/>
3<br/>
4<br/>
5<br/>
6<br/>
7<br/>
8<br/>
9<br/>
10<br/>
11<br/>
12<br/>
13<br/>
14<br/>
15<br/>
16<br/>
17<br/>
18<br/>
19<br/>
20<br/>
21<br/>
22<br/>
23<br/>
24<br/>
25<br/>
26<br/>
27<br/>
28<br/>
29<br/>
30<br/>
31<br/>
32<br/>
33<br/>
34<br/>
35<br/>
36<br/>
37<br/>
38<br/>
39<br/>
40<br/>
41<br/>
42<br/>
43<br/>
44<br/>
45<br/>
46<br/>
47<br/>
48<br/>
49<br/>
50<br/>
51<br/>
52<br/>
53<br/>
54<br/>
55<br/>
56<br/>
57<br/>
58<br/>
59<br/>
60<br/>
61<br/>
62<br/>
63<br/>
64<br/>
65<br/>
66<br/>
67<br/>
68<br/>
69<br/>
70<br/>
71<br/>
72<br/>
73<br/>
74<br/>
75<br/>
76<br/>
77<br/>
78<br/>
79<br/>
80<br/>
81<br/>
82<br/>
83<br/>
84<br/>
85<br/>
86<br/>
87<br/>
88<br/>
89<br/>
90<br/>
91<br/>
92<br/>
93<br/>
94<br/>
95<br/>
96<br/>
97<br/>
98<br/>
99<br/>
100<br/>
101<br/>
102<br/>
103<br/>
104<br/>
105<br/>
106<br/>
107<br/>
108<br/>
109<br/>
110<br/>
111<br/>
112<br/>
113<br/>
114<br/>
115<br/>
116<br/>
117<br/>
118<br/>
119<br/>
120<br/>
121<br/>
122<br/>
123<br/>
124<br/>
125<br/>
126<br/>
127<br/>
128<br/>
129<br/>
130<br/>
131<br/>
132<br/>
133<br/>
134<br/>
135<br/>
136<br/>
137<br/>
138<br/>
139<br/>
140<br/>
141<br/>
142<br/>
143<br/>
144<br/>
145<br/>
146<br/>
147<br/>
148<br/>
149<br/>
150<br/>
151<br/>
152<br/>
153<br/>
154<br/>
155<br/>
156<br/>
157<br/>
158<br/>
159<br/>
160<br/>
161<br/>
162<br/>
163<br/>
164<br/>
165<br/>
166<br/>
167<br/>
168<br/>
169<br/>
170<br/>
171<br/>
172<br/>
173<br/>
174<br/>
175<br/>
176<br/>
177<br/>
178<br/>
179<br/>
180<br/>
181<br/>
182<br/>
183<br/>
184<br/>
185<br/>
186<br/>
187<br/>
188<br/>
189<br/>
190<br/>
191<br/>
192<br/>
193<br/>
194<br/>
195<br/>
196<br/>
197<br/>
198<br/>
199<br/>
200<br/>
201<br/>
202<br/>
203<br/>
204<br/>
205<br/>
206<br/>
207<br/>
208<br/>
209<br/>
210<br/>
211<br/>
212<br/>
213<br/>
214<br/>
215<br/>
216<br/>
217<br/>
218<br/>
219<br/>
220<br/>
221<br/>
222<br/>
223<br/>
224<br/>
225<br/>
226<br/>
227<br/>
228<br/>
229<br/>
230<br/>
231<br/>
232<br/>
233<br/>
234<br/>
235<br/>
236<br/>
237<br/>
238<br/>
239<br/>
240<br/>
241<br/>
242<br/>
243<br/>
244<br/>
245<br/>
246<br/>
247<br/>
248<br/>
249<br/>
250<br/>
251<br/>
252<br/>
253<br/>
254<br/>
255<br/>
256<br/>
257<br/>
258<br/>
259<br/>
260<br/>
261<br/>
262<br/>
263<br/>
264<br/>
265<br/>
266<br/>
267<br/>
268<br/>
269<br/>
270<br/>
271<br/>
272<br/>
273<br/>
274<br/>
275<br/>
276<br/>
277<br/>
278<br/>
279<br/>
280<br/>
281<br/>
282<br/>
283<br/>
284<br/>
285<br/>
286<br/>
287<br/>
288<br/>
289<br/>
290<br/>
291<br/>
292<br/>
293<br/>
294<br/>
295<br/>
296<br/>
297<br/>
298<br/>
299<br/>
300<br/>
301<br/>
302<br/>
303<br/>
304<br/>
305<br/>
306<br/>
307<br/>
308<br/>
309<br/>
310<br/>
311<br/>
312<br/>
313<br/>
314<br/>
315<br/>
316<br/>
317<br/>
318<br/>
319<br/>
320<br/>
321<br/>
322<br/>
323<br/>
324<br/>
325<br/>
326<br/>
327<br/>
328<br/>
329<br/>
330<br/>
331<br/>
332<br/>
333<br/>
334<br/>
335<br/>
336<br/>
337<br/>
338<br/>
339<br/>
340<br/>
341<br/>
342<br/>
343<br/>
344<br/>
345<br/>
346<br/>
347<br/>
348<br/>
349<br/>
350<br/>
351<br/>
352<br/>
353<br/>
354<br/>
355<br/>
356<br/>
357<br/>
358<br/>
359<br/>
360<br/>
361<br/>
362<br/>
363<br/>
364<br/>
365<br/>
366<br/>
367<br/>
368<br/>
369<br/>
370<br/>
371<br/>
372<br/>
373<br/>
374<br/>
375<br/>
376<br/>
377<br/>
378<br/>
379<br/>
380<br/>
381<br/>
382<br/>
383<br/>
384<br/>
385<br/>
386<br/>
387<br/>
388<br/>
389<br/>
390<br/>
391<br/>
392<br/>
393<br/>
394<br/>
395<br/>
396<br/>
397<br/>
398<br/>
399<br/>
400<br/>
401<br/>
402<br/>
403<br/>
404<br/>
405<br/>
406<br/>
407<br/>
408<br/>
409<br/>
410<br/>
411<br/>
412<br/>
413<br/>
414<br/>
415<br/>
416<br/>
417<br/>
418<br/>
419<br/>
420<br/>
421<br/>
422<br/>
423<br/>
424<br/>
425<br/>
426<br/>
427<br/>
428<br/>
429<br/>
430<br/>
431<br/>
432<br/>
433<br/>
434<br/>
435<br/>
436<br/>
437<br/>
438<br/>
439<br/>
440<br/>
441<br/>
442<br/>
443<br/>
444<br/>
445<br/>
446<br/>
447<br/>
448<br/>
449<br/>
450<br/>
451<br/>
452<br/>
453<br/>
454<br/>
455<br/>
456<br/>
457<br/>
458<br/>
459<br/>
460<br/>
461<br/>
462<br/>
463<br/>
464<br/>
465<br/>
466<br/>
467<br/>
468<br/>
469<br/>
470<br/>
471<br/>
472<br/>
473<br/>
474<br/>
475<br/>
476<br/>
477<br/>
478<br/>
479<br/>
480<br/>
481<br/>
482<br/>
483<br/>
484<br/>
485<br/>
486<br/>
487<br/>
488<br/>
489<br/>
490<br/>
491<br/>
492<br/>
493<br/>
494<br/>
495<br/>
496<br/>
497<br/>
498<br/>
499<br/>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<!-- The purpose of the 'id' on the HTML element is to get something
identifiable to show up in the root scroll frame's content description,
so we can check it for layerization. -->
<html id="outer3">
<head>
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
</head>
<body>
<div id="inner3" class="inner-frame">
<div class="inner-content"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<!-- The purpose of the 'id' on the HTML element is to get something
identifiable to show up in the root scroll frame's content description,
so we can check it for layerization. -->
<html id="outer4">
<head>
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
</head>
<body>
<div id="inner4" class="inner-frame">
<div class="inner-content"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity panning test for scrollable div</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function scrollOuter() {
var outer = document.getElementById('outer');
var transformEnd = function() {
SpecialPowers.Services.obs.removeObserver(transformEnd, "APZ:TransformEnd", false);
dump("Transform complete; flushing repaints...\n");
flushApzRepaints(checkScroll, outer.contentWindow);
};
SpecialPowers.Services.obs.addObserver(transformEnd, "APZ:TransformEnd", false);
synthesizeNativeTouchDrag(outer.contentDocument.body, 10, 100, 0, -(50 + TOUCH_SLOP));
dump("Finished native drag, waiting for transform-end observer...\n");
}
function checkScroll() {
var outerScroll = document.getElementById('outer').contentWindow.scrollY;
is(outerScroll, 50, "check that the iframe scrolled");
subtestDone();
}
waitUntilApzStable().then(scrollOuter);
</script>
</head>
<body>
<iframe id="outer" style="height: 250px; border: solid 1px black" src="data:text/html,<body style='height:5000px'>"></iframe>
<div style="height: 5000px; background-color: lightgreen;">
This div makes the top-level page scrollable.
</div>
</body>
</html>

View file

@ -0,0 +1,81 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Ensure we get a touch-cancel after a contextmenu comes up</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function longPressLink() {
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
dump("Finished synthesizing touch-start, waiting for events...\n");
});
}
var eventsFired = 0;
function recordEvent(e) {
if (getPlatform() == "windows") {
// On Windows we get a mouselongtap event once the long-tap has been detected
// by APZ, and that's what we use as the trigger to lift the finger. That then
// triggers the contextmenu. This matches the platform convention.
switch (eventsFired) {
case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
case 1:
is(e.type, 'mouselongtap', 'Got a mouselongtap');
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
break;
case 2: is(e.type, 'touchend', 'Got a touchend'); break;
case 3: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
default: ok(false, 'Got an unexpected event of type ' + e.type); break;
}
eventsFired++;
if (eventsFired == 4) {
dump("Finished waiting for events, doing an APZ flush to see if any more unexpected events come through...\n");
flushApzRepaints(function() {
dump("Done APZ flush, ending test...\n");
subtestDone();
});
}
} else {
// On non-Windows platforms we get a contextmenu event once the long-tap has
// been detected. Since we prevent-default that, we don't get a mouselongtap
// event at all, and instead get a touchcancel.
switch (eventsFired) {
case 0: is(e.type, 'touchstart', 'Got a touchstart'); break;
case 1: is(e.type, 'contextmenu', 'Got a contextmenu'); e.preventDefault(); break;
case 2: is(e.type, 'touchcancel', 'Got a touchcancel'); break;
default: ok(false, 'Got an unexpected event of type ' + e.type); break;
}
eventsFired++;
if (eventsFired == 3) {
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end, doing an APZ flush to see if any more unexpected events come through...\n");
flushApzRepaints(function() {
dump("Done APZ flush, ending test...\n");
subtestDone();
});
});
}
}
}
window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
window.addEventListener('touchend', recordEvent, { passive: true, capture: true });
window.addEventListener('touchcancel', recordEvent, true);
window.addEventListener('contextmenu', recordEvent, true);
SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
waitUntilApzStable()
.then(longPressLink);
</script>
</head>
<body>
<a id="b" href="#">Link to nowhere</a>
</body>
</html>

View file

@ -0,0 +1,46 @@
<head>
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Wheel-scrolling over inactive subframe with perspective</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var subframe = document.getElementById('scroll');
// scroll over the middle of the subframe, to make sure it scrolls,
// not the page
var scrollPos = subframe.scrollTop;
yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
<style>
#scroll {
width: 200px;
height: 200px;
overflow: scroll;
perspective: 400px;
}
#scrolled {
width: 200px;
height: 1000px; /* so the subframe has room to scroll */
background: linear-gradient(red, blue); /* so you can see it scroll */
transform: translateZ(0px); /* so the perspective makes it to the display list */
}
</style>
</head>
<body>
<div id="scroll">
<div id="scrolled"></div>
</div>
<div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
</body>
</head>

View file

@ -0,0 +1,47 @@
<head>
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Wheel-scrolling over inactive subframe with z-index</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var subframe = document.getElementById('scroll');
// scroll over the middle of the subframe, and make sure that it scrolls,
// not the page
var scrollPos = subframe.scrollTop;
yield moveMouseAndScrollWheelOver(subframe, 100, 100, testDriver);
dump("after scroll, subframe.scrollTop = " + subframe.scrollTop + "\n");
ok(subframe.scrollTop > scrollPos, "subframe scrolled after wheeling over it");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
<style>
#scroll {
width: 200px;
height: 200px;
overflow: scroll;
}
#scrolled {
width: 200px;
height: 1000px; /* so the subframe has room to scroll */
z-index: 2;
background: linear-gradient(red, blue); /* so you can see it scroll */
transform: translateZ(0px); /* to force active layers */
will-change: transform; /* to force active layers */
}
</style>
</head>
<body>
<div id="scroll">
<div id="scrolled"></div>
</div>
<div style="height: 5000px;"></div><!-- So the page is scrollable as well -->
</body>
</head>

View file

@ -0,0 +1,62 @@
<head>
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Wheel-scrolling over position:fixed and position:sticky elements, in the top-level document as well as iframes</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function* test(testDriver) {
var iframeWin = document.getElementById('iframe').contentWindow;
// scroll over the middle of the iframe's position:sticky element, check
// that it scrolls the iframe
var scrollPos = iframeWin.scrollY;
yield moveMouseAndScrollWheelOver(iframeWin.document.body, 50, 150, testDriver);
ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:sticky element");
// same, but using the iframe's position:fixed element
scrollPos = iframeWin.scrollY;
yield moveMouseAndScrollWheelOver(iframeWin.document.body, 250, 150, testDriver);
ok(iframeWin.scrollY > scrollPos, "iframe scrolled after wheeling over the position:fixed element");
// same, but scrolling the scrollable frame *inside* the position:fixed item
var fpos = document.getElementById('fpos_scrollable');
scrollPos = fpos.scrollTop;
yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled");
// wait for it to layerize fully and then try again
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
scrollPos = fpos.scrollTop;
yield moveMouseAndScrollWheelOver(fpos, 50, 150, testDriver);
ok(fpos.scrollTop > scrollPos, "scrollable item inside fixed-pos element scrolled after layerization");
// same, but using the top-level window's position:sticky element
scrollPos = window.scrollY;
yield moveMouseAndScrollWheelOver(document.body, 50, 150, testDriver);
ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:sticky element");
// same, but using the top-level window's position:fixed element
scrollPos = window.scrollY;
yield moveMouseAndScrollWheelOver(document.body, 250, 150, testDriver);
ok(window.scrollY > scrollPos, "top-level document scrolled after wheeling over the position:fixed element");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>
<body style="height:5000px; margin:0">
<div style="position:sticky; width: 100px; height: 300px; top: 0; background-color:red">sticky</div>
<div style="position:fixed; width: 100px; height: 300px; top: 0; left: 200px; background-color: green">fixed</div>
<iframe id='iframe' width="300" height="400" src="data:text/html,<body style='height:5000px; margin:0'><div style='position:sticky; width:100px; height:300px; top: 0; background-color:red'>sticky</div><div style='position:fixed; right:0; top: 0; width:100px; height:300px; background-color:green'>fixed</div>"></iframe>
<div id="fpos_scrollable" style="position:fixed; width: 100px; height: 300px; top: 0; left: 400px; background-color: red; overflow:scroll">
<div style="background-color: blue; height: 1000px; margin: 3px">scrollable content inside a fixed-pos item</div>
</div>
</body>
</head>

View file

@ -0,0 +1,61 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity touch-tapping test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function startTest() {
if (window.scrollY == 0) {
// the scrollframe is not yet marked as APZ-scrollable. Mark it so and
// start over.
window.scrollTo(0, 1);
waitForApzFlushedRepaints(startTest);
return;
}
// This is a scroll by 20px that should use paint-skipping if possible.
// If paint-skipping is enabled, this should not trigger a paint, but go
// directly to the compositor using an empty transaction. We check for this
// by ensuring the document element did not get painted.
var utils = window.opener.SpecialPowers.getDOMWindowUtils(window);
var elem = document.documentElement;
var skipping = location.search == '?true';
utils.checkAndClearPaintedState(elem);
window.scrollTo(0, 20);
waitForAllPaints(function() {
if (skipping) {
is(utils.checkAndClearPaintedState(elem), false, "Document element didn't get painted");
}
// After that's done, we click on the button to make sure the
// skipped-paint codepath still has working APZ event transformations.
clickButton();
});
}
function clickButton() {
document.addEventListener('click', clicked, false);
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
});
}
function clicked(e) {
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
subtestDone();
}
waitUntilApzStable().then(startTest);
</script>
</head>
<body style="height: 5000px">
<div style="height: 50px">spacer</div>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>

View file

@ -0,0 +1,15 @@
body {
height: 500px;
}
.inner-frame {
margin-top: 50px; /* this should be at least 30px */
height: 200%;
width: 75%;
overflow: scroll;
}
.inner-content {
height: 200%;
width: 200%;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}

View file

@ -0,0 +1,504 @@
<html id="tall_html">
<body>
This is a tall page<br/>
1<br/>
2<br/>
3<br/>
4<br/>
5<br/>
6<br/>
7<br/>
8<br/>
9<br/>
10<br/>
11<br/>
12<br/>
13<br/>
14<br/>
15<br/>
16<br/>
17<br/>
18<br/>
19<br/>
20<br/>
21<br/>
22<br/>
23<br/>
24<br/>
25<br/>
26<br/>
27<br/>
28<br/>
29<br/>
30<br/>
31<br/>
32<br/>
33<br/>
34<br/>
35<br/>
36<br/>
37<br/>
38<br/>
39<br/>
40<br/>
41<br/>
42<br/>
43<br/>
44<br/>
45<br/>
46<br/>
47<br/>
48<br/>
49<br/>
50<br/>
51<br/>
52<br/>
53<br/>
54<br/>
55<br/>
56<br/>
57<br/>
58<br/>
59<br/>
60<br/>
61<br/>
62<br/>
63<br/>
64<br/>
65<br/>
66<br/>
67<br/>
68<br/>
69<br/>
70<br/>
71<br/>
72<br/>
73<br/>
74<br/>
75<br/>
76<br/>
77<br/>
78<br/>
79<br/>
80<br/>
81<br/>
82<br/>
83<br/>
84<br/>
85<br/>
86<br/>
87<br/>
88<br/>
89<br/>
90<br/>
91<br/>
92<br/>
93<br/>
94<br/>
95<br/>
96<br/>
97<br/>
98<br/>
99<br/>
100<br/>
101<br/>
102<br/>
103<br/>
104<br/>
105<br/>
106<br/>
107<br/>
108<br/>
109<br/>
110<br/>
111<br/>
112<br/>
113<br/>
114<br/>
115<br/>
116<br/>
117<br/>
118<br/>
119<br/>
120<br/>
121<br/>
122<br/>
123<br/>
124<br/>
125<br/>
126<br/>
127<br/>
128<br/>
129<br/>
130<br/>
131<br/>
132<br/>
133<br/>
134<br/>
135<br/>
136<br/>
137<br/>
138<br/>
139<br/>
140<br/>
141<br/>
142<br/>
143<br/>
144<br/>
145<br/>
146<br/>
147<br/>
148<br/>
149<br/>
150<br/>
151<br/>
152<br/>
153<br/>
154<br/>
155<br/>
156<br/>
157<br/>
158<br/>
159<br/>
160<br/>
161<br/>
162<br/>
163<br/>
164<br/>
165<br/>
166<br/>
167<br/>
168<br/>
169<br/>
170<br/>
171<br/>
172<br/>
173<br/>
174<br/>
175<br/>
176<br/>
177<br/>
178<br/>
179<br/>
180<br/>
181<br/>
182<br/>
183<br/>
184<br/>
185<br/>
186<br/>
187<br/>
188<br/>
189<br/>
190<br/>
191<br/>
192<br/>
193<br/>
194<br/>
195<br/>
196<br/>
197<br/>
198<br/>
199<br/>
200<br/>
201<br/>
202<br/>
203<br/>
204<br/>
205<br/>
206<br/>
207<br/>
208<br/>
209<br/>
210<br/>
211<br/>
212<br/>
213<br/>
214<br/>
215<br/>
216<br/>
217<br/>
218<br/>
219<br/>
220<br/>
221<br/>
222<br/>
223<br/>
224<br/>
225<br/>
226<br/>
227<br/>
228<br/>
229<br/>
230<br/>
231<br/>
232<br/>
233<br/>
234<br/>
235<br/>
236<br/>
237<br/>
238<br/>
239<br/>
240<br/>
241<br/>
242<br/>
243<br/>
244<br/>
245<br/>
246<br/>
247<br/>
248<br/>
249<br/>
250<br/>
251<br/>
252<br/>
253<br/>
254<br/>
255<br/>
256<br/>
257<br/>
258<br/>
259<br/>
260<br/>
261<br/>
262<br/>
263<br/>
264<br/>
265<br/>
266<br/>
267<br/>
268<br/>
269<br/>
270<br/>
271<br/>
272<br/>
273<br/>
274<br/>
275<br/>
276<br/>
277<br/>
278<br/>
279<br/>
280<br/>
281<br/>
282<br/>
283<br/>
284<br/>
285<br/>
286<br/>
287<br/>
288<br/>
289<br/>
290<br/>
291<br/>
292<br/>
293<br/>
294<br/>
295<br/>
296<br/>
297<br/>
298<br/>
299<br/>
300<br/>
301<br/>
302<br/>
303<br/>
304<br/>
305<br/>
306<br/>
307<br/>
308<br/>
309<br/>
310<br/>
311<br/>
312<br/>
313<br/>
314<br/>
315<br/>
316<br/>
317<br/>
318<br/>
319<br/>
320<br/>
321<br/>
322<br/>
323<br/>
324<br/>
325<br/>
326<br/>
327<br/>
328<br/>
329<br/>
330<br/>
331<br/>
332<br/>
333<br/>
334<br/>
335<br/>
336<br/>
337<br/>
338<br/>
339<br/>
340<br/>
341<br/>
342<br/>
343<br/>
344<br/>
345<br/>
346<br/>
347<br/>
348<br/>
349<br/>
350<br/>
351<br/>
352<br/>
353<br/>
354<br/>
355<br/>
356<br/>
357<br/>
358<br/>
359<br/>
360<br/>
361<br/>
362<br/>
363<br/>
364<br/>
365<br/>
366<br/>
367<br/>
368<br/>
369<br/>
370<br/>
371<br/>
372<br/>
373<br/>
374<br/>
375<br/>
376<br/>
377<br/>
378<br/>
379<br/>
380<br/>
381<br/>
382<br/>
383<br/>
384<br/>
385<br/>
386<br/>
387<br/>
388<br/>
389<br/>
390<br/>
391<br/>
392<br/>
393<br/>
394<br/>
395<br/>
396<br/>
397<br/>
398<br/>
399<br/>
400<br/>
401<br/>
402<br/>
403<br/>
404<br/>
405<br/>
406<br/>
407<br/>
408<br/>
409<br/>
410<br/>
411<br/>
412<br/>
413<br/>
414<br/>
415<br/>
416<br/>
417<br/>
418<br/>
419<br/>
420<br/>
421<br/>
422<br/>
423<br/>
424<br/>
425<br/>
426<br/>
427<br/>
428<br/>
429<br/>
430<br/>
431<br/>
432<br/>
433<br/>
434<br/>
435<br/>
436<br/>
437<br/>
438<br/>
439<br/>
440<br/>
441<br/>
442<br/>
443<br/>
444<br/>
445<br/>
446<br/>
447<br/>
448<br/>
449<br/>
450<br/>
451<br/>
452<br/>
453<br/>
454<br/>
455<br/>
456<br/>
457<br/>
458<br/>
459<br/>
460<br/>
461<br/>
462<br/>
463<br/>
464<br/>
465<br/>
466<br/>
467<br/>
468<br/>
469<br/>
470<br/>
471<br/>
472<br/>
473<br/>
474<br/>
475<br/>
476<br/>
477<br/>
478<br/>
479<br/>
480<br/>
481<br/>
482<br/>
483<br/>
484<br/>
485<br/>
486<br/>
487<br/>
488<br/>
489<br/>
490<br/>
491<br/>
492<br/>
493<br/>
494<br/>
495<br/>
496<br/>
497<br/>
498<br/>
499<br/>
</body>
</html>

View file

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity touch-tapping test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function clickButton() {
document.addEventListener('click', clicked, false);
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
});
}
function clicked(e) {
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
subtestDone();
}
waitUntilApzStable().then(clickButton);
</script>
</head>
<body>
<button id="b" style="width: 10px; height: 10px"></button>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity touch-tapping test with fullzoom</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function clickButton() {
document.addEventListener('click', clicked, false);
synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
dump("Finished synthesizing tap, waiting for button to be clicked...\n");
});
}
function clicked(e) {
is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
subtestDone();
}
SpecialPowers.setFullZoom(window, 2.0);
waitUntilApzStable().then(clickButton);
</script>
</head>
<body>
<button id="b" style="width: 10px; height: 10px; position: relative; top: 100px"></button>
</body>
</html>

View file

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Ensure APZ doesn't wait for passive listeners</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
var touchdownTime;
function longPressLink() {
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, function() {
dump("Finished synthesizing touch-start, waiting for events...\n");
});
}
var touchstartReceived = false;
function recordEvent(e) {
if (!touchstartReceived) {
touchstartReceived = true;
is(e.type, 'touchstart', 'Got a touchstart');
e.preventDefault(); // should be a no-op because it's a passive listener
return;
}
// If APZ decides to wait for the content response on a particular input block,
// it needs to wait until both the touchstart and touchmove event are handled
// by the main thread. In this case there is no touchmove at all, so APZ would
// end up waiting indefinitely and time out the test. The fact that we get this
// contextmenu event (mouselongtap on Windows) at all means that APZ decided
// not to wait for the content response, which is the desired behaviour, since
// the touchstart listener was registered as a passive listener.
if (getPlatform() == "windows") {
is(e.type, 'mouselongtap', 'Got a mouselongtap');
} else {
is(e.type, 'contextmenu', 'Got a contextmenu');
}
e.preventDefault();
synthesizeNativeTouch(document.getElementById('b'), 5, 5, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, function() {
dump("Finished synthesizing touch-end to clear state; finishing test...\n");
subtestDone();
});
}
window.addEventListener('touchstart', recordEvent, { passive: true, capture: true });
if (getPlatform() == "windows") {
SpecialPowers.addChromeEventListener('mouselongtap', recordEvent, true);
} else {
window.addEventListener('contextmenu', recordEvent, true);
}
waitUntilApzStable()
.then(longPressLink);
</script>
</head>
<body>
<a id="b" href="#">Link to nowhere</a>
</body>
</html>

View file

@ -0,0 +1,115 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Sanity touch-action test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function checkScroll(x, y, desc) {
is(window.scrollX, x, desc + " - x axis");
is(window.scrollY, y, desc + " - y axis");
}
function* test(testDriver) {
var target = document.getElementById('target');
document.body.addEventListener('touchend', testDriver, { passive: true });
// drag the page up to scroll down by 50px
yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
"Synthesized native vertical drag (1), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(0, 50, "After first vertical drag, with pan-y" );
// switch style to pan-x
document.body.style.touchAction = 'pan-x';
ok(true, "Waiting for pan-x to propagate...");
yield waitForAllPaintsFlushed(function() {
flushApzRepaints(testDriver);
});
// drag the page up to scroll down by 50px, but it won't happen because pan-x
yield ok(synthesizeNativeTouchDrag(target, 10, 100, 0, -(50 + TOUCH_SLOP)),
"Synthesized native vertical drag (2), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(0, 50, "After second vertical drag, with pan-x");
// drag the page left to scroll right by 50px
yield ok(synthesizeNativeTouchDrag(target, 100, 10, -(50 + TOUCH_SLOP), 0),
"Synthesized horizontal drag (1), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(50, 50, "After first horizontal drag, with pan-x");
// drag the page diagonally right/down to scroll up/left by 40px each axis;
// only the x-axis will actually scroll because pan-x
yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
"Synthesized diagonal drag (1), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(10, 50, "After first diagonal drag, with pan-x");
// switch style back to pan-y
document.body.style.touchAction = 'pan-y';
ok(true, "Waiting for pan-y to propagate...");
yield waitForAllPaintsFlushed(function() {
flushApzRepaints(testDriver);
});
// drag the page diagonally right/down to scroll up/left by 40px each axis;
// only the y-axis will actually scroll because pan-y
yield ok(synthesizeNativeTouchDrag(target, 10, 10, (40 + TOUCH_SLOP), (40 + TOUCH_SLOP)),
"Synthesized diagonal drag (2), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(10, 10, "After second diagonal drag, with pan-y");
// switch style to none
document.body.style.touchAction = 'none';
ok(true, "Waiting for none to propagate...");
yield waitForAllPaintsFlushed(function() {
flushApzRepaints(testDriver);
});
// drag the page diagonally up/left to scroll down/right by 40px each axis;
// neither will scroll because of touch-action
yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
"Synthesized diagonal drag (3), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(10, 10, "After third diagonal drag, with none");
document.body.style.touchAction = 'manipulation';
ok(true, "Waiting for manipulation to propagate...");
yield waitForAllPaintsFlushed(function() {
flushApzRepaints(testDriver);
});
// drag the page diagonally up/left to scroll down/right by 40px each axis;
// both will scroll because of touch-action
yield ok(synthesizeNativeTouchDrag(target, 100, 100, -(40 + TOUCH_SLOP), -(40 + TOUCH_SLOP)),
"Synthesized diagonal drag (4), waiting for touch-end event...");
yield flushApzRepaints(testDriver);
checkScroll(50, 50, "After fourth diagonal drag, with manipulation");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>
<body style="touch-action: pan-y">
<div style="width: 5000px; height: 5000px; background-color: lightgreen;">
This div makes the page scrollable on both axes.<br>
This is the second line of text.<br>
This is the third line of text.<br>
This is the fourth line of text.
</div>
<!-- This fixed-position div remains in the same place relative to the browser chrome, so we
can use it as a targeting device for synthetic touch events. The body will move around
as we scroll, so we'd have to be constantly adjusting the synthetic drag coordinates
if we used that as the target element. -->
<div style="position:fixed; left: 10px; top: 10px; width: 1px; height: 1px" id="target"></div>
</body>
</html>

View file

@ -0,0 +1,143 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Complex touch-action test</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function checkScroll(target, x, y, desc) {
is(target.scrollLeft, x, desc + " - x axis");
is(target.scrollTop, y, desc + " - y axis");
}
function resetConfiguration(config, testDriver) {
// Cycle through all the configuration_X elements, setting them to display:none
// except for when X == config, in which case set it to display:block
var i = 0;
while (true) {
i++;
var element = document.getElementById('configuration_' + i);
if (element == null) {
if (i <= config) {
ok(false, "The configuration requested was not encountered!");
}
break;
}
if (i == config) {
element.style.display = 'block';
} else {
element.style.display = 'none';
}
}
// Also reset the scroll position on the scrollframe
var s = document.getElementById('scrollframe');
s.scrollLeft = 0;
s.scrollTop = 0;
return waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
}
function* test(testDriver) {
var scrollframe = document.getElementById('scrollframe');
document.body.addEventListener('touchend', testDriver, { passive: true });
// Helper function for the tests below.
// Touch-pan configuration |configuration| towards scroll offset (dx, dy) with
// the pan touching down at (x, y). Check that the final scroll offset is
// (ex, ey). |desc| is some description string.
function* scrollAndCheck(configuration, x, y, dx, dy, ex, ey, desc) {
// Start with a clean slate
yield resetConfiguration(configuration, testDriver);
// Figure out the panning deltas
if (dx != 0) {
dx = -(dx + TOUCH_SLOP);
}
if (dy != 0) {
dy = -(dy + TOUCH_SLOP);
}
// Do the pan
yield ok(synthesizeNativeTouchDrag(scrollframe, x, y, dx, dy),
"Synthesized drag of (" + dx + ", " + dy + ") on configuration " + configuration);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Check for expected scroll position
checkScroll(scrollframe, ex, ey, 'configuration ' + configuration + ' ' + desc);
}
// Test configuration_1, which contains two sibling elements that are
// overlapping. The touch-action from the second sibling (which is on top)
// should be used for the overlapping area.
yield* scrollAndCheck(1, 25, 75, 20, 0, 20, 0, "first element horizontal scroll");
yield* scrollAndCheck(1, 25, 75, 0, 50, 0, 0, "first element vertical scroll");
yield* scrollAndCheck(1, 75, 75, 50, 0, 0, 0, "overlap horizontal scroll");
yield* scrollAndCheck(1, 75, 75, 0, 50, 0, 50, "overlap vertical scroll");
yield* scrollAndCheck(1, 125, 75, 20, 0, 0, 0, "second element horizontal scroll");
yield* scrollAndCheck(1, 125, 75, 0, 50, 0, 50, "second element vertical scroll");
// Test configuration_2, which contains two overlapping elements with a
// parent/child relationship. The parent has pan-x and the child has pan-y,
// which means that panning on the parent should work horizontally only, and
// on the child no panning should occur at all.
yield* scrollAndCheck(2, 125, 125, 50, 50, 0, 0, "child scroll");
yield* scrollAndCheck(2, 75, 75, 50, 50, 0, 0, "overlap scroll");
yield* scrollAndCheck(2, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
yield* scrollAndCheck(2, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
// Test configuration_3, which is the same as configuration_2, except the child
// has a rotation transform applied. This forces the event regions on the two
// elements to be built separately and then get merged.
yield* scrollAndCheck(3, 125, 125, 50, 50, 0, 0, "child scroll");
yield* scrollAndCheck(3, 75, 75, 50, 50, 0, 0, "overlap scroll");
yield* scrollAndCheck(3, 25, 75, 0, 50, 0, 0, "parent vertical scroll");
yield* scrollAndCheck(3, 75, 25, 50, 0, 50, 0, "parent horizontal scroll");
// Test configuration_4 has two elements, one above the other, not overlapping,
// and the second element is a child of the first. The parent has pan-x, the
// child has pan-y, but that means panning horizontally on the parent should
// work and panning in any direction on the child should not do anything.
yield* scrollAndCheck(4, 75, 75, 50, 50, 50, 0, "parent diagonal scroll");
yield* scrollAndCheck(4, 75, 150, 50, 50, 0, 0, "child diagonal scroll");
}
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
</script>
</head>
<body>
<div id="scrollframe" style="width: 300px; height: 300px; overflow:scroll">
<div id="scrolled_content" style="width: 1000px; height: 1000px; background-color: green">
</div>
<div id="configuration_1" style="display:none; position: relative; top: -1000px">
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue"></div>
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: -100px; left: 50px; background-color: yellow"></div>
</div>
<div id="configuration_2" style="display:none; position: relative; top: -1000px">
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow"></div>
</div>
</div>
<div id="configuration_3" style="display:none; position: relative; top: -1000px">
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 50px; left: 50px; background-color: yellow; transform: rotate(90deg)"></div>
</div>
</div>
<div id="configuration_4" style="display:none; position: relative; top: -1000px">
<div style="touch-action: pan-x; width: 100px; height: 100px; background-color: blue">
<div style="touch-action: pan-y; width: 100px; height: 100px; position: relative; top: 125px; background-color: yellow"></div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,246 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<title>Test to ensure APZ doesn't always wait for touch-action</title>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript">
function failure(e) {
ok(false, "This event listener should not have triggered: " + e.type);
}
function success(e) {
success.triggered = true;
}
// This helper function provides a way for the child process to synchronously
// check how many touch events the chrome process main-thread has processed. This
// function can be called with three values: 'start', 'report', and 'end'.
// The 'start' invocation sets up the listeners, and should be invoked before
// the touch events of interest are generated. This should only be called once.
// This returns true on success, and false on failure.
// The 'report' invocation can be invoked multiple times, and returns an object
// (in JSON string format) containing the counters.
// The 'end' invocation tears down the listeners, and should be invoked once
// at the end to clean up. Returns true on success, false on failure.
function chromeTouchEventCounter(operation) {
function chromeProcessCounter() {
addMessageListener('start', function() {
Components.utils.import('resource://gre/modules/Services.jsm');
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
if (typeof topWin.eventCounts != 'undefined') {
dump('Found pre-existing eventCounts object on the top window!\n');
return false;
}
topWin.eventCounts = { 'touchstart': 0, 'touchmove': 0, 'touchend': 0 };
topWin.counter = function(e) {
topWin.eventCounts[e.type]++;
}
topWin.addEventListener('touchstart', topWin.counter, { passive: true });
topWin.addEventListener('touchmove', topWin.counter, { passive: true });
topWin.addEventListener('touchend', topWin.counter, { passive: true });
return true;
});
addMessageListener('report', function() {
Components.utils.import('resource://gre/modules/Services.jsm');
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
return JSON.stringify(topWin.eventCounts);
});
addMessageListener('end', function() {
Components.utils.import('resource://gre/modules/Services.jsm');
var topWin = Services.wm.getMostRecentWindow('navigator:browser');
if (typeof topWin.eventCounts == 'undefined') {
dump('The eventCounts object was not found on the top window!\n');
return false;
}
topWin.removeEventListener('touchstart', topWin.counter);
topWin.removeEventListener('touchmove', topWin.counter);
topWin.removeEventListener('touchend', topWin.counter);
delete topWin.counter;
delete topWin.eventCounts;
return true;
});
}
if (typeof chromeTouchEventCounter.chromeHelper == 'undefined') {
// This is the first time getSnapshot is being called; do initialization
chromeTouchEventCounter.chromeHelper = SpecialPowers.loadChromeScript(chromeProcessCounter);
SimpleTest.registerCleanupFunction(function() { chromeTouchEventCounter.chromeHelper.destroy() });
}
return chromeTouchEventCounter.chromeHelper.sendSyncMessage(operation, "");
}
// Simple wrapper that waits until the chrome process has seen |count| instances
// of the |eventType| event. Returns true on success, and false if 10 seconds
// go by without the condition being satisfied.
function waitFor(eventType, count) {
var start = Date.now();
while (JSON.parse(chromeTouchEventCounter('report'))[eventType] != count) {
if (Date.now() - start > 10000) {
// It's taking too long, let's abort
return false;
}
}
return true;
}
function* test(testDriver) {
// The main part of this test should run completely before the child process'
// main-thread deals with the touch event, so check to make sure that happens.
document.body.addEventListener('touchstart', failure, { passive: true });
// What we want here is to synthesize all of the touch events (from this code in
// the child process), and have the chrome process generate and process them,
// but not allow the events to be dispatched back into the child process until
// later. This allows us to ensure that the APZ in the chrome process is not
// waiting for the child process to send notifications upon processing the
// events. If it were doing so, the APZ would block and this test would fail.
// In order to actually implement this, we call the synthesize functions with
// a async callback in between. The synthesize functions just queue up a
// runnable on the child process main thread and return immediately, so with
// the async callbacks, the child process main thread queue looks like
// this after we're done setting it up:
// synthesizeTouchStart
// callback testDriver
// synthesizeTouchMove
// callback testDriver
// ...
// synthesizeTouchEnd
// callback testDriver
//
// If, after setting up this queue, we yield once, the first synthesization and
// callback will run - this will send a synthesization message to the chrome
// process, and return control back to us right away. When the chrome process
// processes with the synthesized event, it will dispatch the DOM touch event
// back to the child process over IPC, which will go into the end of the child
// process main thread queue, like so:
// synthesizeTouchStart (done)
// invoke testDriver (done)
// synthesizeTouchMove
// invoke testDriver
// ...
// synthesizeTouchEnd
// invoke testDriver
// handle DOM touchstart <-- touchstart goes at end of queue
//
// As we continue yielding one at a time, the synthesizations run, and the
// touch events get added to the end of the queue. As we yield, we take
// snapshots in the chrome process, to make sure that the APZ has started
// scrolling even though we know we haven't yet processed the DOM touch events
// in the child process yet.
//
// Note that the "async callback" we use here is SpecialPowers.executeSoon,
// because nothing else does exactly what we want:
// - setTimeout(..., 0) does not maintain ordering, because it respects the
// time delta provided (i.e. the callback can jump the queue to meet its
// deadline).
// - SpecialPowers.spinEventLoop and SpecialPowers.executeAfterFlushingMessageQueue
// are not e10s friendly, and can get arbitrarily delayed due to IPC
// round-trip time.
// - SimpleTest.executeSoon has a codepath that delegates to setTimeout, so
// is less reliable if it ever decides to switch to that codepath.
// The other problem we need to deal with is the asynchronicity in the chrome
// process. That is, we might request a snapshot before the chrome process has
// actually synthesized the event and processed it. To guard against this, we
// register a thing in the chrome process that counts the touch events that
// have been dispatched, and poll that thing synchronously in order to make
// sure we only snapshot after the event in question has been processed.
// That's what the chromeTouchEventCounter business is all about. The sync
// polling looks bad but in practice only ends up needing to poll once or
// twice before the condition is satisfied, and as an extra precaution we add
// a time guard so it fails after 10s of polling.
// So, here we go...
// Set up the chrome process touch listener
ok(chromeTouchEventCounter('start'), "Chrome touch counter registered");
// Set up the child process events and callbacks
var scroller = document.getElementById('scroller');
synthesizeNativeTouch(scroller, 10, 110, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
SpecialPowers.executeSoon(testDriver);
for (var i = 1; i < 10; i++) {
synthesizeNativeTouch(scroller, 10, 110 - (i * 10), SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, null, 0);
SpecialPowers.executeSoon(testDriver);
}
synthesizeNativeTouch(scroller, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, null, 0);
SpecialPowers.executeSoon(testDriver);
ok(true, "Finished setting up event queue");
// Get our baseline snapshot
var rect = rectRelativeToScreen(scroller);
var lastSnapshot = getSnapshot(rect);
ok(true, "Got baseline snapshot");
yield; // this will tell the chrome process to synthesize the touchstart event
// and then we wait to make sure it got processed:
ok(waitFor('touchstart', 1), "Touchstart processed in chrome process");
// Loop through the touchmove events
for (var i = 1; i < 10; i++) {
yield;
ok(waitFor('touchmove', i), "Touchmove processed in chrome process");
var snapshot = getSnapshot(rect);
if (i == 1) {
// The first touchmove is consumed to get us into the panning state, so
// no actual panning occurs
ok(lastSnapshot == snapshot, "Snapshot 1 was the same as baseline");
} else {
ok(lastSnapshot != snapshot, "Snapshot " + i + " was different from the previous one");
}
lastSnapshot = snapshot;
}
// Wait for the touchend as well, just for good form
yield;
ok(waitFor('touchend', 1), "Touchend processed in chrome process");
// Clean up the chrome process hooks
chromeTouchEventCounter('end');
// Now we are going to release our grip on the child process main thread,
// so that all the DOM events that were queued up can be processed. We
// register a touchstart listener to make sure this happens.
document.body.removeEventListener('touchstart', failure);
document.body.addEventListener('touchstart', success, { passive: true });
yield flushApzRepaints(testDriver);
ok(success.triggered, "The touchstart event handler was triggered after snapshotting completed");
document.body.removeEventListener('touchstart', success);
}
if (SpecialPowers.isMainProcess()) {
// This is probably android, where everything is single-process. The
// test structure depends on e10s, so the test won't run properly on
// this platform. Skip it
ok(true, "Skipping test because it is designed to run from the content process");
subtestDone();
} else {
waitUntilApzStable()
.then(runContinuation(test))
.then(subtestDone);
}
</script>
</head>
<body>
<div id="scroller" style="width: 400px; height: 400px; overflow: scroll; touch-action: pan-y">
<div style="width: 200px; height: 200px; background-color: lightgreen;">
This is a colored div that will move on the screen as the scroller scrolls.
</div>
<div style="width: 1000px; height: 1000px; background-color: lightblue">
This is a large div to make the scroller scrollable.
</div>
</body>
</html>

View file

@ -0,0 +1,67 @@
[DEFAULT]
support-files =
apz_test_native_event_utils.js
apz_test_utils.js
helper_basic_pan.html
helper_bug982141.html
helper_bug1151663.html
helper_bug1162771.html
helper_bug1271432.html
helper_bug1280013.html
helper_bug1285070.html
helper_bug1299195.html
helper_click.html
helper_div_pan.html
helper_drag_click.html
helper_drag_scroll.html
helper_iframe_pan.html
helper_iframe1.html
helper_iframe2.html
helper_long_tap.html
helper_scroll_inactive_perspective.html
helper_scroll_inactive_zindex.html
helper_scroll_on_position_fixed.html
helper_scrollto_tap.html
helper_subframe_style.css
helper_tall.html
helper_tap.html
helper_tap_fullzoom.html
helper_tap_passive.html
helper_touch_action.html
helper_touch_action_regions.html
helper_touch_action_complex.html
tags = apz
[test_bug982141.html]
[test_bug1151663.html]
[test_bug1151667.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_bug1253683.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_bug1277814.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_bug1304689.html]
[test_bug1304689-2.html]
[test_frame_reconstruction.html]
[test_group_mouseevents.html]
skip-if = (toolkit == 'android') # mouse events not supported on mobile
[test_group_pointerevents.html]
[test_group_touchevents.html]
[test_group_wheelevents.html]
skip-if = (toolkit == 'android') # wheel events not supported on mobile
[test_group_zoom.html]
skip-if = (toolkit != 'android') # only android supports zoom
[test_interrupted_reflow.html]
[test_layerization.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_inactive_bug1190112.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_inactive_flattened_frame.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_scroll_subframe_scrollbar.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_touch_listeners_impacting_wheel.html]
skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
[test_wheel_scroll.html]
skip-if = (os == 'android') # wheel events not supported on mobile
[test_wheel_transactions.html]
skip-if = (os == 'android') # wheel events not supported on mobile

View file

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1151663</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
// Run the actual test in its own window, because it requires that the
// root APZC be scrollable. Mochitest pages themselves often run
// inside an iframe which means we have no control over the root APZC.
var w = null;
window.onload = function() {
pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
w = window.open("helper_bug1151663.html", "_blank");
});
};
}
function finishTest() {
w.close();
SimpleTest.finish();
};
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151663">Mozilla Bug 1151663</a>
</body>
</html>

View file

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1151667
-->
<head>
<title>Test for Bug 1151667</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
#subframe {
margin-top: 100px;
height: 500px;
width: 500px;
overflow: scroll;
}
#subframe-content {
height: 1000px;
width: 500px;
/* the background is so that we can see it scroll*/
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
#page-content {
height: 5000px;
width: 500px;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151667">Mozilla Bug 1151667</a>
<p id="display"></p>
<div id="subframe">
<!-- This makes sure the subframe is scrollable -->
<div id="subframe-content"></div>
</div>
<!-- This makes sure the page is also scrollable, so it (rather than the subframe)
is considered the primary async-scrollable frame, and so the subframe isn't
layerized upon page load. -->
<div id="page-content"></div>
<pre id="test">
<script type="application/javascript;version=1.7">
function startTest() {
var subframe = document.getElementById('subframe');
synthesizeNativeWheelAndWaitForScrollEvent(subframe, 100, 150, 0, -10, continueTest);
}
function continueTest() {
var subframe = document.getElementById('subframe');
is(subframe.scrollTop > 0, true, "We should have scrolled the subframe down");
is(document.documentElement.scrollTop, 0, "We should not have scrolled the page");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
waitUntilApzStable().then(startTest);
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1253683
-->
<head>
<title>Test to ensure non-scrollable frames don't get layerized</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<p id="display"></p>
<div id="container" style="height: 500px; overflow:scroll">
<pre id="no_layer" style="background-color: #f5f5f5; margin: 15px; padding: 15px; margin-top: 100px; border: 1px solid #eee; overflow:scroll">sample code here</pre>
<div style="height: 5000px">spacer to make the 'container' div the root scrollable element</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
function* test(testDriver) {
var container = document.getElementById('container');
var no_layer = document.getElementById('no_layer');
// Check initial state
is(container.scrollTop, 0, "Initial scrollY should be 0");
ok(!isLayerized('no_layer'), "initially 'no_layer' should not be layerized");
// Scrolling over outer1 should layerize outer1, but not inner1.
yield moveMouseAndScrollWheelOver(no_layer, 10, 10, testDriver, true);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
ok(container.scrollTop > 0, "We should have scrolled the body");
ok(!isLayerized('no_layer'), "no_layer should still not be layerized");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
// Turn off displayport expiry so that we don't miss failures where the
// displayport is set and expired before we check for layerization.
// Also enable APZ test logging, since we use that data to determine whether
// a scroll frame was layerized.
pushPrefs([["apz.displayport_expiry_ms", 0],
["apz.test.logging_enabled", true]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,106 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1277814
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1277814</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
function* test(testDriver) {
// Trigger the buggy scenario
var subframe = document.getElementById('bug1277814-div');
subframe.classList.add('a');
// The transform change is animated, so let's step through 1s of animation
var utils = SpecialPowers.getDOMWindowUtils(window);
for (var i = 0; i < 60; i++) {
utils.advanceTimeAndRefresh(16);
}
utils.restoreNormalRefresh();
// Wait for the layer tree with any updated dispatch-to-content region to
// get pushed over to the APZ
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Trigger layerization of the subframe by scrolling the wheel over it
yield moveMouseAndScrollWheelOver(subframe, 10, 10, testDriver);
// Give APZ the chance to compute a displayport, and content
// to render based on it.
yield waitForApzFlushedRepaints(testDriver);
// Examine the content-side APZ test data
var contentTestData = utils.getContentAPZTestData();
// Test that the scroll frame for the div 'bug1277814-div' appears in
// the APZ test data. The bug this test is for causes the displayport
// calculation for this scroll frame to go wrong, causing it not to
// become layerized.
contentTestData = convertTestData(contentTestData);
var foundIt = false;
for (var seqNo in contentTestData.paints) {
var paint = contentTestData.paints[seqNo];
for (var scrollId in paint) {
var scrollFrame = paint[scrollId];
if ('contentDescription' in scrollFrame &&
scrollFrame['contentDescription'].includes('bug1277814-div')) {
foundIt = true;
}
}
}
SimpleTest.ok(foundIt, "expected to find APZ test data for bug1277814-div");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
pushPrefs([["apz.test.logging_enabled", true]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
<style>
#bug1277814-div
{
position: absolute;
left: 0;
top: 0;
padding: .5em;
overflow: auto;
color: white;
background: green;
max-width: 30em;
max-height: 6em;
visibility: hidden;
transform: scaleY(0);
transition: transform .15s ease-out, visibility 0s ease .15s;
}
#bug1277814-div.a
{
visibility: visible;
transform: scaleY(1);
transition: transform .15s ease-out;
}
</style>
</head>
<body>
<!-- Use a unique id because we'll be checking for it in the content
description logged in the APZ test data -->
<div id="bug1277814-div">
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
CoolCmd<br>CoolCmd<br>CoolCmd<br>CoolCmd<br>
<button>click me</button>
</body>
</html>

View file

@ -0,0 +1,131 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1285070</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
#outer {
height: 400px;
width: 415px;
overflow: scroll;
position: relative;
scroll-behavior: smooth;
}
#outer.contentBefore::before {
top: 0;
content: '';
display: block;
height: 2px;
position: absolute;
width: 100%;
z-index: 99;
}
</style>
<script type="application/javascript">
function* test(testDriver) {
var utils = SpecialPowers.DOMWindowUtils;
var elm = document.getElementById('outer');
// Set margins on the element, to ensure it is layerized
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Take control of the refresh driver
utils.advanceTimeAndRefresh(0);
// Start a smooth-scroll animation in the compositor and let it go a few
// frames, so that there is some "user scrolling" going on (per the comment
// in AsyncPanZoomController::NotifyLayersUpdated)
elm.scrollTop = 10;
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
// Do another scroll update but also do a frame reconstruction within the same
// tick of the refresh driver.
elm.scrollTop = 100;
elm.classList.add('contentBefore');
// Now let everything settle and all the animations run out
for (var i = 0; i < 60; i++) {
utils.advanceTimeAndRefresh(16);
}
utils.restoreNormalRefresh();
yield flushApzRepaints(testDriver);
is(elm.scrollTop, 100, "The scrollTop now should be y=100");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
pushPrefs([["apz.displayport_expiry_ms", 0]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</head>
<body>
<div id="outer">
<div id="inner">
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,135 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1304689
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1285070</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
#outer {
height: 400px;
width: 415px;
overflow: scroll;
position: relative;
scroll-behavior: smooth;
}
#outer.instant {
scroll-behavior: auto;
}
#outer.contentBefore::before {
top: 0;
content: '';
display: block;
height: 2px;
position: absolute;
width: 100%;
z-index: 99;
}
</style>
<script type="application/javascript">
function* test(testDriver) {
var utils = SpecialPowers.DOMWindowUtils;
var elm = document.getElementById('outer');
// Set margins on the element, to ensure it is layerized
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, /*priority*/ 1);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Take control of the refresh driver
utils.advanceTimeAndRefresh(0);
// Start a smooth-scroll animation in the compositor and let it go a few
// frames, so that there is some "user scrolling" going on (per the comment
// in AsyncPanZoomController::NotifyLayersUpdated)
elm.scrollTop = 10;
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
// Do another scroll update but also do a frame reconstruction within the same
// tick of the refresh driver.
elm.classList.add('instant');
elm.scrollTop = 100;
elm.classList.add('contentBefore');
// Now let everything settle and all the animations run out
for (var i = 0; i < 60; i++) {
utils.advanceTimeAndRefresh(16);
}
utils.restoreNormalRefresh();
yield flushApzRepaints(testDriver);
is(elm.scrollTop, 100, "The scrollTop now should be y=100");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
pushPrefs([["apz.displayport_expiry_ms", 0]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</head>
<body>
<div id="outer">
<div id="inner">
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
this is some scrollable text.<br>
this is a second line to make the scrolling more obvious.<br>
and a third for good measure.<br>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=982141
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 982141</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
// Run the actual test in its own window, because it requires that the
// root APZC not be scrollable. Mochitest pages themselves often run
// inside an iframe which means we have no control over the root APZC.
var w = null;
window.onload = function() {
pushPrefs([["apz.test.logging_enabled", true]]).then(function() {
w = window.open("helper_bug982141.html", "_blank");
});
};
}
function finishTest() {
w.close();
SimpleTest.finish();
};
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
</body>
</html>

View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1235899
-->
<head>
<title>Test for bug 1235899</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
.outer {
height: 400px;
width: 415px;
overflow: hidden;
position: relative;
}
.inner {
height: 100%;
outline: none;
overflow-x: hidden;
overflow-y: scroll;
position: relative;
scroll-behavior: smooth;
}
.outer.contentBefore::before {
top: 0;
content: '';
display: block;
height: 2px;
position: absolute;
width: 100%;
z-index: 99;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1235899">Mozilla Bug 1235899</a>
<p id="display"></p>
<div id="content">
<p>You should be able to fling this list without it stopping abruptly</p>
<div class="outer">
<div class="inner">
<ol>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
<li>Some text</li>
</ol>
</div>
</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
function* test(testDriver) {
var elm = document.getElementsByClassName('inner')[0];
elm.scrollTop = 0;
yield flushApzRepaints(testDriver);
// Take over control of the refresh driver and compositor
var utils = SpecialPowers.DOMWindowUtils;
utils.advanceTimeAndRefresh(0);
// Kick off an APZ smooth-scroll to 0,200
elm.scrollTo(0, 200);
yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
// Let's do a couple of frames of the animation, and make sure it's going
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
yield flushApzRepaints(testDriver);
ok(elm.scrollTop > 0, "APZ animation in progress", "scrollTop is now " + elm.scrollTop);
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
var frameReconstructionTriggered = 0;
// Register the listener that triggers the frame reconstruction
elm.onscroll = function() {
// Do the reconstruction
elm.parentNode.classList.add('contentBefore');
frameReconstructionTriggered++;
// schedule a thing to undo the changes above
setTimeout(function() {
elm.parentNode.classList.remove('contentBefore');
}, 0);
}
// and do a few more frames of the animation, this should trigger the listener
// and the frame reconstruction
utils.advanceTimeAndRefresh(16);
utils.advanceTimeAndRefresh(16);
yield flushApzRepaints(testDriver);
ok(elm.scrollTop < 200, "APZ animation not yet completed", "scrollTop is now " + elm.scrollTop);
ok(frameReconstructionTriggered > 0, "Frame reconstruction triggered", "reconstruction triggered " + frameReconstructionTriggered + " times");
// and now run to completion
for (var i = 0; i < 100; i++) {
utils.advanceTimeAndRefresh(16);
}
utils.restoreNormalRefresh();
yield waitForAllPaints(function() { setTimeout(testDriver, 0); });
yield flushApzRepaints(testDriver);
is(elm.scrollTop, 200, "Element should have scrolled by 200px");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
SimpleTest.expectAssertions(0, 1); // this test triggers an assertion, see bug 1247050
waitUntilApzStable()
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Various mouse tests that spawn in new windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var subtests = [
// Sanity test to synthesize a mouse click
{'file': 'helper_click.html?dtc=false'},
// Same as above, but with a dispatch-to-content region that exercises the
// main-thread notification codepaths for mouse events
{'file': 'helper_click.html?dtc=true'},
// Sanity test for click but with some mouse movement between the down and up
{'file': 'helper_drag_click.html'},
// Test for dragging on a fake-scrollbar element that scrolls the page
{'file': 'helper_drag_scroll.html'}
];
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1285070
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1285070</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var subtests = [
{'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
{'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
];
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Various touch tests that spawn in new windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var basic_pan_prefs = [
// Dropping the touch slop to 0 makes the tests easier to write because
// we can just do a one-pixel drag to get over the pan threshold rather
// than having to hard-code some larger value.
["apz.touch_start_tolerance", "0.0"],
// The touchstart from the drag can turn into a long-tap if the touch-move
// events get held up. Try to prevent that by making long-taps require
// a 10 second hold. Note that we also cannot enable chaos mode on this
// test for this reason, since chaos mode can cause the long-press timer
// to fire sooner than the pref dictates.
["ui.click_hold_context_menus.delay", 10000],
// The subtests in this test do touch-drags to pan the page, but we don't
// want those pans to turn into fling animations, so we increase the
// fling min velocity requirement absurdly high.
["apz.fling_min_velocity_threshold", "10000"],
// The helper_div_pan's div gets a displayport on scroll, but if the
// test takes too long the displayport can expire before the new scroll
// position is synced back to the main thread. So we disable displayport
// expiry for these tests.
["apz.displayport_expiry_ms", 0],
];
var touch_action_prefs = basic_pan_prefs.slice(); // make a copy
touch_action_prefs.push(["layout.css.touch_action.enabled", true]);
var isWindows = (getPlatform() == "windows");
var subtests = [
// Simple tests to exercise basic panning behaviour
{'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
{'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
{'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},
// Simple test to exercise touch-tapping behaviour
{'file': 'helper_tap.html'},
// Tapping, but with a full-zoom applied
{'file': 'helper_tap_fullzoom.html'},
// For the following two tests, disable displayport suppression to make sure it
// doesn't interfere with the test by scheduling paints non-deterministically.
{'file': 'helper_scrollto_tap.html?true', 'prefs': [["apz.paint_skipping.enabled", true]], 'dp_suppression': false},
{'file': 'helper_scrollto_tap.html?false', 'prefs': [["apz.paint_skipping.enabled", false]], 'dp_suppression': false},
// Taps on media elements to make sure the touchend event is delivered
// properly. We increase the long-tap timeout to ensure it doesn't get trip
// during the tap.
// Also this test (on Windows) cannot satisfy the OS requirement of providing
// an injected touch event every 100ms, because it waits for a paint between
// the touchstart and the touchend, so we have to use the "fake injection"
// code instead.
{'file': 'helper_bug1162771.html', 'prefs': [["ui.click_hold_context_menus.delay", 10000],
["apz.test.fails_with_native_injection", isWindows]]},
// As with the previous test, this test cannot inject touch events every 100ms
// because it waits for a long-tap, so we have to use the "fake injection" code
// instead.
{'file': 'helper_long_tap.html', 'prefs': [["apz.test.fails_with_native_injection", isWindows]]},
// For the following test, we want to make sure APZ doesn't wait for a content
// response that is never going to arrive. To detect this we set the content response
// timeout to a day, so that the entire test times out and fails if APZ does
// end up waiting.
{'file': 'helper_tap_passive.html', 'prefs': [["apz.content_response_timeout", 24 * 60 * 60 * 1000],
["apz.test.fails_with_native_injection", isWindows]]},
// Simple test to exercise touch-action CSS property
{'file': 'helper_touch_action.html', 'prefs': touch_action_prefs},
// More complex touch-action tests, with overlapping regions and such
{'file': 'helper_touch_action_complex.html', 'prefs': touch_action_prefs},
// Tests that touch-action CSS properties are handled in APZ without waiting
// on the main-thread, when possible
{'file': 'helper_touch_action_regions.html', 'prefs': touch_action_prefs},
];
if (isApzEnabled()) {
ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
if (getPlatform() == "android") {
// This has a lot of subtests, and Android emulators are slow.
SimpleTest.requestLongerTimeout(2);
}
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Various wheel-scrolling tests that spawn in new windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var prefs = [
// turn off smooth scrolling so that we don't have to wait for
// APZ animations to finish before sampling the scroll offset
['general.smoothScroll', false],
// ensure that any mouse movement will trigger a new wheel transaction,
// because in this test we move the mouse a bunch and want to recalculate
// the target APZC after each such movement.
['mousewheel.transaction.ignoremovedelay', 0],
['mousewheel.transaction.timeout', 0]
]
var subtests = [
{'file': 'helper_scroll_on_position_fixed.html', 'prefs': prefs},
{'file': 'helper_bug1271432.html', 'prefs': prefs},
{'file': 'helper_scroll_inactive_perspective.html', 'prefs': prefs},
{'file': 'helper_scroll_inactive_zindex.html', 'prefs': prefs}
];
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Various zoom-related tests that spawn in new windows</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
var prefs = [
// We need the APZ paint logging information
["apz.test.logging_enabled", true],
// Dropping the touch slop to 0 makes the tests easier to write because
// we can just do a one-pixel drag to get over the pan threshold rather
// than having to hard-code some larger value.
["apz.touch_start_tolerance", "0.0"],
// The subtests in this test do touch-drags to pan the page, but we don't
// want those pans to turn into fling animations, so we increase the
// fling-stop threshold velocity to absurdly high.
["apz.fling_stopped_threshold", "10000"],
// The helper_bug1280013's div gets a displayport on scroll, but if the
// test takes too long the displayport can expire before we read the value
// out of the test. So we disable displayport expiry for these tests.
["apz.displayport_expiry_ms", 0],
];
var subtests = [
{'file': 'helper_bug1280013.html', 'prefs': prefs},
];
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
window.onload = function() {
runSubtestsSeriallyInFreshWindows(subtests)
.then(SimpleTest.finish);
};
}
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,719 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1292781
-->
<head>
<title>Test for bug 1292781</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
.outer {
height: 400px;
width: 415px;
overflow: hidden;
position: relative;
}
.inner {
height: 100%;
outline: none;
overflow-x: hidden;
overflow-y: scroll;
position: relative;
}
.inner div:nth-child(even) {
background-color: lightblue;
}
.inner div:nth-child(odd) {
background-color: lightgreen;
}
.outer.contentBefore::before {
top: 0;
content: '';
display: block;
height: 2px;
position: absolute;
width: 100%;
z-index: 99;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292781">Mozilla Bug 1292781</a>
<p id="display"></p>
<div id="content">
<p>The frame reconstruction should not leave this scrollframe in a bad state</p>
<div class="outer">
<div class="inner">
this is the top of the scrollframe.
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
this is near the top of the scrollframe.
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
this is near the bottom of the scrollframe.
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
<div>this is a box</div>
this is the bottom of the scrollframe.
</div>
</div>
</div>
<pre id="test">
<script type="text/javascript">
// Returns a list of async scroll offsets that the |inner| element had, one for
// each paint.
function getAsyncScrollOffsets(aPaintsToIgnore) {
var offsets = [];
var compositorTestData = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData();
var buckets = compositorTestData.paints.slice(aPaintsToIgnore);
ok(buckets.length >= 3, "Expected at least three paints in the compositor test data");
var childIsLayerized = false;
for (var i = 0; i < buckets.length; ++i) {
var apzcTree = buildApzcTree(convertScrollFrameData(buckets[i].scrollFrames));
var rcd = findRcdNode(apzcTree);
if (rcd == null) {
continue;
}
if (rcd.children.length > 0) {
// The child may not be layerized in the first few paints, but once it is
// layerized, it should stay layerized.
childIsLayerized = true;
}
if (!childIsLayerized) {
continue;
}
ok(rcd.children.length == 1, "Root content APZC has exactly one child");
var scroll = rcd.children[0].asyncScrollOffset;
var pieces = scroll.replace(/[()\s]+/g, '').split(',');
is(pieces.length, 2, "expected string of form (x,y)");
offsets.push({ x: parseInt(pieces[0]),
y: parseInt(pieces[1]) });
}
return offsets;
}
function* test(testDriver) {
var utils = SpecialPowers.DOMWindowUtils;
// The APZ test data accumulates whenever a test turns it on. We just want
// the data for this test, so we check how many frames are already recorded
// and discard those later.
var framesToSkip = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData().paints.length;
var elm = document.getElementsByClassName('inner')[0];
// Set a zero-margin displayport to ensure that the element is async-scrollable
// otherwise on Fennec it is not
utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, 0);
var maxScroll = elm.scrollTopMax;
elm.scrollTop = maxScroll;
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Take control of the refresh driver
utils.advanceTimeAndRefresh(0);
// Force the next reflow to get interrupted
utils.forceReflowInterrupt();
// Make a change that triggers frame reconstruction, and then tick the refresh
// driver so that layout processes the pending restyles and then runs an
// interruptible reflow. That reflow *will* be interrupted (because of the flag
// we set above), and we should end up with a transient 0,0 scroll offset
// being sent to the compositor.
elm.parentNode.classList.add('contentBefore');
utils.advanceTimeAndRefresh(0);
// On android, and maybe non-e10s platforms generally, we need to manually
// kick the paint to send the layer transaction to the compositor.
yield waitForAllPaints(function() { setTimeout(testDriver, 0) });
// Read the main-thread scroll offset; although this is temporarily 0,0 that
// temporary value is never exposed to content - instead reading this value
// will finish doing the interrupted reflow from above and then report the
// correct scroll offset.
is(elm.scrollTop, maxScroll, "Main-thread scroll position was restored");
// .. and now flush everything to make sure the state gets pushed over to the
// compositor and APZ as well.
utils.restoreNormalRefresh();
yield waitForApzFlushedRepaints(testDriver);
// Now we pull the compositor data and check it. What we expect to see is that
// the scroll position goes to maxScroll, then drops to 0 and then goes back
// to maxScroll. This test is specifically testing that last bit - that it
// properly gets restored from 0 to maxScroll.
// The one hitch is that on Android this page is loaded with some amount of
// zoom, and the async scroll is in ParentLayerPixel coordinates, so it will
// not match maxScroll exactly. Since we can't reliably compute what that
// ParentLayer scroll will be, we just make sure the async scroll is nonzero
// and use the first value we encounter to verify that it got restored properly.
// The other alternative is to spawn this test into a new window with 1.0 zoom
// but I'm tired of doing that for pretty much every test.
var state = 0;
var asyncScrollOffsets = getAsyncScrollOffsets(framesToSkip);
dump("Got scroll offsets: " + JSON.stringify(asyncScrollOffsets) + "\n");
var maxScrollParentLayerPixels = maxScroll;
while (asyncScrollOffsets.length > 0) {
let offset = asyncScrollOffsets.shift();
switch (state) {
// 0 is the initial state, the scroll offset might be zero but should
// become non-zero from when we set scrollTop to scrollTopMax
case 0:
if (offset.y == 0) {
break;
}
if (getPlatform() == "android") {
ok(offset.y > 0, "Async scroll y of scrollframe is " + offset.y);
maxScrollParentLayerPixels = offset.y;
} else {
is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe is " + offset.y);
}
state = 1;
break;
// state 1 starts out at maxScrollParentLayerPixels, should drop to 0
// because of the interrupted reflow putting the scroll into a transient
// zero state
case 1:
if (offset.y == maxScrollParentLayerPixels) {
break;
}
is(offset.y, 0, "Async scroll position was temporarily 0");
state = 2;
break;
// state 2 starts out the transient 0 scroll offset, and we expect the
// scroll position to get restored back to maxScrollParentLayerPixels
case 2:
if (offset.y == 0) {
break;
}
is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe restored to " + offset.y);
state = 3;
break;
// Terminal state. The scroll position should stay at maxScrollParentLayerPixels
case 3:
is(offset.y, maxScrollParentLayerPixels, "Scroll position maintained");
break;
}
}
is(state, 3, "The scroll position did drop to 0 and then get restored properly");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
pushPrefs([["apz.test.logging_enabled", true],
["apz.displayport_expiry_ms", 0]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</body>
</html>

View file

@ -0,0 +1,214 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1173580
-->
<head>
<title>Test for layerization</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<link rel="stylesheet" type="text/css" href="helper_subframe_style.css"/>
<style>
#container {
display: flex;
overflow: scroll;
height: 500px;
}
.outer-frame {
height: 500px;
overflow: scroll;
flex-basis: 100%;
background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
}
#container-content {
height: 200%;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173580">APZ layerization tests</a>
<p id="display"></p>
<div id="container">
<div id="outer1" class="outer-frame">
<div id="inner1" class="inner-frame">
<div class="inner-content"></div>
</div>
</div>
<div id="outer2" class="outer-frame">
<div id="inner2" class="inner-frame">
<div class="inner-content"></div>
</div>
</div>
<iframe id="outer3" class="outer-frame" src="helper_iframe1.html"></iframe>
<iframe id="outer4" class="outer-frame" src="helper_iframe2.html"></iframe>
<!-- The container-content div ensures 'container' is scrollable, so the
optimization that layerizes the primary async-scrollable frame on page
load layerizes it rather than its child subframes. -->
<div id="container-content"></div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
// Scroll the mouse wheel over |element|.
function scrollWheelOver(element, waitForScroll, testDriver) {
moveMouseAndScrollWheelOver(element, 10, 10, testDriver, waitForScroll);
}
const DISPLAYPORT_EXPIRY = 100;
// This helper function produces another helper function, which, when invoked,
// invokes the provided testDriver argument in a setTimeout 0. This is really
// just useful in cases when there are no paints pending, because then
// waitForAllPaints will invoke its callback synchronously. If we did
// waitForAllPaints(testDriver) that might cause reentrancy into the testDriver
// which is bad. This function works around that.
function asyncWrapper(testDriver) {
return function() {
setTimeout(testDriver, 0);
};
}
function* test(testDriver) {
// Initially, nothing should be layerized.
ok(!isLayerized('outer1'), "initially 'outer1' should not be layerized");
ok(!isLayerized('inner1'), "initially 'inner1' should not be layerized");
ok(!isLayerized('outer2'), "initially 'outer2' should not be layerized");
ok(!isLayerized('inner2'), "initially 'inner2' should not be layerized");
ok(!isLayerized('outer3'), "initially 'outer3' should not be layerized");
ok(!isLayerized('inner3'), "initially 'inner3' should not be layerized");
ok(!isLayerized('outer4'), "initially 'outer4' should not be layerized");
ok(!isLayerized('inner4'), "initially 'inner4' should not be layerized");
// Scrolling over outer1 should layerize outer1, but not inner1.
yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
ok(isLayerized('outer1'), "scrolling 'outer1' should cause it to be layerized");
ok(!isLayerized('inner1'), "scrolling 'outer1' should not cause 'inner1' to be layerized");
// Scrolling over inner2 should layerize both outer2 and inner2.
yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
ok(isLayerized('inner2'), "scrolling 'inner2' should cause it to be layerized");
ok(isLayerized('outer2'), "scrolling 'inner2' should also cause 'outer2' to be layerized");
// The second half of the test repeats the same checks as the first half,
// but with an iframe as the outer scrollable frame.
// Scrolling over outer3 should layerize outer3, but not inner3.
yield scrollWheelOver(document.getElementById('outer3').contentDocument.documentElement, true, testDriver);
ok(isLayerized('outer3'), "scrolling 'outer3' should cause it to be layerized");
ok(!isLayerized('inner3'), "scrolling 'outer3' should not cause 'inner3' to be layerized");
// Scrolling over outer4 should layerize both outer4 and inner4.
yield scrollWheelOver(document.getElementById('outer4').contentDocument.getElementById('inner4'), true, testDriver);
ok(isLayerized('inner4'), "scrolling 'inner4' should cause it to be layerized");
ok(isLayerized('outer4'), "scrolling 'inner4' should also cause 'outer4' to be layerized");
// Now we enable displayport expiry, and verify that things are still
// layerized as they were before.
yield SpecialPowers.pushPrefEnv({"set": [["apz.displayport_expiry_ms", DISPLAYPORT_EXPIRY]]}, testDriver);
ok(isLayerized('outer1'), "outer1 is still layerized after enabling expiry");
ok(!isLayerized('inner1'), "inner1 is still not layerized after enabling expiry");
ok(isLayerized('outer2'), "outer2 is still layerized after enabling expiry");
ok(isLayerized('inner2'), "inner2 is still layerized after enabling expiry");
ok(isLayerized('outer3'), "outer3 is still layerized after enabling expiry");
ok(!isLayerized('inner3'), "inner3 is still not layerized after enabling expiry");
ok(isLayerized('outer4'), "outer4 is still layerized after enabling expiry");
ok(isLayerized('inner4'), "inner4 is still layerized after enabling expiry");
// Now we trigger a scroll on some of the things still layerized, so that
// the displayport expiry gets triggered.
// Expire displayport with scrolling on outer1
yield scrollWheelOver(document.getElementById('outer1'), true, testDriver);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(!isLayerized('outer1'), "outer1 is no longer layerized after displayport expiry");
ok(!isLayerized('inner1'), "inner1 is still not layerized after displayport expiry");
// Expire displayport with scrolling on inner2
yield scrollWheelOver(document.getElementById('inner2'), true, testDriver);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Once the expiry elapses, it will trigger expiry on outer2, so we check
// both, one at a time.
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(!isLayerized('inner2'), "inner2 is no longer layerized after displayport expiry");
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(!isLayerized('outer2'), "outer2 got de-layerized with inner2");
// Scroll on inner3. inner3 isn't layerized, and this will cause it to
// get layerized, but it will also trigger displayport expiration for inner3
// which will eventually trigger displayport expiration on inner3 and outer3.
// Note that the displayport expiration might actually happen before the wheel
// input is processed in the compositor (see bug 1246480 comment 3), and so
// we make sure not to wait for a scroll event here, since it may never fire.
// However, if we do get a scroll event while waiting for the expiry, we need
// to restart the expiry timer because the displayport expiry got reset. There's
// no good way that I can think of to deterministically avoid doing this.
let inner3 = document.getElementById('outer3').contentDocument.getElementById('inner3');
yield scrollWheelOver(inner3, false, testDriver);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
var timerId = setTimeout(testDriver, DISPLAYPORT_EXPIRY);
var timeoutResetter = function() {
ok(true, "Got a scroll event; resetting timer...");
clearTimeout(timerId);
setTimeout(testDriver, DISPLAYPORT_EXPIRY);
// by not updating timerId we ensure that this listener resets the timeout
// at most once.
};
inner3.addEventListener('scroll', timeoutResetter, false);
yield; // wait for the setTimeout to elapse
inner3.removeEventListener('scroll', timeoutResetter, false);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(!isLayerized('outer3'), "outer3 is no longer layerized after inner3 triggered expiry");
// Scroll outer4 and wait for the expiry. It should NOT get expired because
// inner4 is still layerized
yield scrollWheelOver(document.getElementById('outer4').contentDocument.documentElement, true, testDriver);
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Wait for the expiry to elapse
yield setTimeout(testDriver, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(asyncWrapper(testDriver));
ok(isLayerized('inner4'), "inner4 is still layerized because it never expired");
ok(isLayerized('outer4'), "outer4 is still layerized because inner4 is still layerized");
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("we are testing code that measures an actual timeout");
SimpleTest.expectAssertions(0, 8); // we get a bunch of "ASSERTION: Bounds computation mismatch" sometimes (bug 1232856)
// Disable smooth scrolling, because it results in long-running scroll
// animations that can result in a 'scroll' event triggered by an earlier
// wheel event as corresponding to a later wheel event.
// Also enable APZ test logging, since we use that data to determine whether
// a scroll frame was layerized.
pushPrefs([["general.smoothScroll", false],
["apz.displayport_expiry_ms", 0],
["apz.test.logging_enabled", true]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,541 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test scrolling flattened inactive frames</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style>
p {
width:200px;
height:200px;
border:solid 1px black;
overflow:auto;
}
</style>
</head>
<body>
<div id="iframe-body" style="overflow: auto; height: 1000px">
<hr>
<hr>
<hr>
<p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p id="subframe">
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p><p>
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p>
</div>
<script clss="testbody" type="text/javascript;version=1.7">
function ScrollTops() {
this.outerScrollTop = document.getElementById('iframe-body').scrollTop;
this.innerScrollTop = document.getElementById('subframe').scrollTop;
}
var DefaultEvent = {
deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0, deltaY: 1,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
};
function test() {
var subframe = document.getElementById('subframe');
var oldpos = new ScrollTops();
sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
var newpos = new ScrollTops();
ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
doOuterScroll(subframe, newpos);
});
}
function doOuterScroll(subframe, oldpos) {
var outer = document.getElementById('iframe-body');
sendWheelAndPaint(outer, 20, 5, DefaultEvent, function () {
var newpos = new ScrollTops();
ok(oldpos.outerScrollTop != newpos.outerScrollTop, "viewport should have scrolled");
ok(oldpos.innerScrollTop == newpos.innerScrollTop, "subframe should not have scrolled");
doInnerScrollAgain(subframe, newpos);
});
}
function doInnerScrollAgain(subframe, oldpos) {
sendWheelAndPaint(subframe, 10, 10, DefaultEvent, function () {
var newpos = new ScrollTops();
ok(oldpos.outerScrollTop == newpos.outerScrollTop, "viewport should not have scrolled");
ok(oldpos.innerScrollTop != newpos.innerScrollTop, "subframe should have scrolled");
SimpleTest.finish();
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
pushPrefs([['general.smoothScroll', false],
['mousewheel.transaction.timeout', 0],
['mousewheel.transaction.ignoremovedelay', 0]])
.then(waitUntilApzStable)
.then(test);
</script>
</body>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test scrolling flattened inactive frames</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<div id="container" style="height: 300px; width: 600px; overflow: auto; background: yellow">
<div id="outer" style="height: 400px; width: 500px; overflow: auto; background: black">
<div id="inner" style="mix-blend-mode: screen; height: 800px; overflow: auto; background: purple">
</div>
</div>
</div>
<script class="testbody" type="text/javascript;version=1.7">
function test() {
var container = document.getElementById('container');
var outer = document.getElementById('outer');
var inner = document.getElementById('inner');
var outerScrollTop = outer.scrollTop;
var containerScrollTop = container.scrollTop;
var event = {
deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0,
deltaY: 10,
lineOrPageDeltaX: 0,
lineOrPageDeltaY: 10,
};
sendWheelAndPaint(inner, 20, 30, event, function () {
ok(container.scrollTop == containerScrollTop, "container scrollframe should not have scrolled");
ok(outer.scrollTop > outerScrollTop, "nested scrollframe should have scrolled");
SimpleTest.finish();
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
pushPrefs([['general.smoothScroll', false],
['mousewheel.transaction.timeout', 1000000]])
.then(waitUntilApzStable)
.then(test);
</script>
</body>
</html>

View file

@ -0,0 +1,117 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test scrolling subframe scrollbars</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<style>
p {
width:200px;
height:200px;
border:solid 1px black;
}
</style>
</head>
<body>
<p id="subframe">
1 <br>
2 <br>
3 <br>
4 <br>
5 <br>
6 <br>
7 <br>
8 <br>
9 <br>
10 <br>
11 <br>
12 <br>
13 <br>
14 <br>
15 <br>
16 <br>
17 <br>
18 <br>
19 <br>
20 <br>
21 <br>
22 <br>
23 <br>
24 <br>
25 <br>
26 <br>
27 <br>
28 <br>
29 <br>
30 <br>
31 <br>
32 <br>
33 <br>
34 <br>
35 <br>
36 <br>
37 <br>
38 <br>
39 <br>
40 <br>
</p>
<script clss="testbody" type="text/javascript;version=1.7">
var DefaultEvent = {
deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 0, deltaY: 1,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
};
var ScrollbarWidth = 0;
function test() {
var subframe = document.getElementById('subframe');
var oldClientWidth = subframe.clientWidth;
subframe.style.overflow = 'auto';
subframe.getBoundingClientRect();
waitForAllPaintsFlushed(function () {
ScrollbarWidth = oldClientWidth - subframe.clientWidth;
if (!ScrollbarWidth) {
// Probably we have overlay scrollbars - abort the test.
ok(true, "overlay scrollbars - skipping test");
SimpleTest.finish();
return;
}
ok(subframe.scrollHeight > subframe.clientHeight, "subframe should have scrollable content");
testScrolling(subframe);
});
}
function testScrolling(subframe) {
// Send a wheel event roughly to where we think the trackbar is. We pick a
// point at the bottom, in the middle of the trackbar, where the slider is
// unlikely to be (since it starts at the top).
var posX = subframe.clientWidth + (ScrollbarWidth / 2);
var posY = subframe.clientHeight - 20;
var oldScrollTop = subframe.scrollTop;
sendWheelAndPaint(subframe, posX, posY, DefaultEvent, function () {
ok(subframe.scrollTop > oldScrollTop, "subframe should have scrolled");
SimpleTest.finish();
});
}
SimpleTest.waitForExplicitFinish();
pushPrefs([['general.smoothScroll', false],
['mousewheel.transaction.timeout', 0],
['mousewheel.transaction.ignoremovedelay', 0]])
.then(waitUntilApzStable)
.then(test);
</script>
</body>
</html>

View file

@ -0,0 +1,77 @@
<html>
<head>
<title>Test Frame Uniformity While Scrolling</title>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<style>
#content {
height: 5000px;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
</style>
<script type="text/javascript">
var scrollEvents = 100;
var i = 0;
var testPref = "gfx.vsync.collect-scroll-transforms";
// Scroll points
var x = 100;
var y = 150;
SimpleTest.waitForExplicitFinish();
var utils = _getDOMWindowUtils(window);
function sendScrollEvent(aRafTimestamp) {
var scrollDiv = document.getElementById("content");
if (i < scrollEvents) {
i++;
// Scroll diff
var dx = 0;
var dy = -10; // Negative to scroll down
synthesizeNativeWheelAndWaitForWheelEvent(scrollDiv, x, y, dx, dy);
window.requestAnimationFrame(sendScrollEvent);
} else {
// Locally, with silk and apz + e10s, retina 15" mbp usually get ~1.0 - 1.5
// w/o silk + e10s + apz, I get up to 7. Lower is better.
// Windows, I get ~3. Values are not valid w/o hardware vsync
var uniformities = _getDOMWindowUtils().getFrameUniformityTestData();
for (var j = 0; j < uniformities.layerUniformities.length; j++) {
var layerResult = uniformities.layerUniformities[j];
var layerAddr = layerResult.layerAddress;
var uniformity = layerResult.frameUniformity;
var msg = "Layer: " + layerAddr.toString(16) + " Uniformity: " + uniformity;
SimpleTest.ok((uniformity >= 0) && (uniformity < 4.0), msg);
}
SimpleTest.finish();
}
}
function startTest() {
window.requestAnimationFrame(sendScrollEvent);
}
window.onload = function() {
var apzEnabled = SpecialPowers.getBoolPref("layers.async-pan-zoom.enabled");
if (!apzEnabled) {
SimpleTest.ok(true, "APZ not enabled, skipping test");
SimpleTest.finish();
}
SpecialPowers.pushPrefEnv({
"set" : [
[testPref, true]
]
}, startTest);
}
</script>
</head>
<body>
<div id="content">
</div>
</body>
</html>

View file

@ -0,0 +1,114 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1203140
-->
<head>
<title>Test for Bug 1203140</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203140">Mozilla Bug 1203140</a>
<p id="display"></p>
<div id="content" style="overflow-y:scroll; height: 400px">
<p>The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.</p>
<div id="box" style="width: 200px; height: 200px; background-color: blue"></div>
<div style="height: 1000px; width: 10px">Div to make 'content' scrollable</div>
</div>
<pre id="test">
<script type="application/javascript">
const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes
function takeSnapshots(e) {
// Grab some snapshots, and make sure some of them are different (i.e. check
// the page is scrolling in the compositor, concurrently with this wheel
// listener running).
// Note that we want this function to take less time than the content response
// timeout, otherwise the scrolling will start even if we haven't returned,
// and that would invalidate purpose of the test.
var start = Date.now();
var lastSnapshot = null;
var success = false;
// Get the position of the 'content' div relative to the screen
var rect = rectRelativeToScreen(document.getElementById('content'));
for (var i = 0; i < 10; i++) {
SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(16);
var snapshot = getSnapshot(rect);
//dump("Took snapshot " + snapshot + "\n"); // this might help with debugging
if (lastSnapshot && lastSnapshot != snapshot) {
ok(true, "Found some different pixels in snapshot " + i + " compared to previous");
success = true;
}
lastSnapshot = snapshot;
}
ok(success, "Found some snapshots that were different");
ok((Date.now() - start) < kResponseTimeoutMs, "Snapshotting ran quickly enough");
// Until now, no scroll events will have been dispatched to content. That's
// because scroll events are dispatched on the main thread, which we've been
// hogging with the code above. At this point we restore the normal refresh
// behaviour and let the main thread go back to C++ code, so the scroll events
// fire and we unwind from the main test continuation.
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
}
function* test(testDriver) {
var box = document.getElementById('box');
// Ensure the div is layerized by scrolling it
yield moveMouseAndScrollWheelOver(box, 10, 10, testDriver);
box.addEventListener('touchstart', function(e) {
ok(false, "This should never be run");
}, false);
box.addEventListener('wheel', takeSnapshots, { capture: false, passive: true });
// Let the event regions and layerization propagate to the APZ
yield waitForAllPaints(function() {
flushApzRepaints(testDriver);
});
// Take over control of the refresh driver and compositor
var utils = SpecialPowers.DOMWindowUtils;
utils.advanceTimeAndRefresh(0);
// Trigger an APZ scroll using a wheel event. If APZ is waiting for a
// content response, it will wait for takeSnapshots to finish running before
// it starts scrolling, which will cause the checks in takeSnapshots to fail.
yield synthesizeNativeMouseMoveAndWaitForMoveEvent(box, 10, 10, testDriver);
yield synthesizeNativeWheelAndWaitForScrollEvent(box, 10, 10, 0, -50, testDriver);
}
if (isApzEnabled()) {
SimpleTest.waitForExplicitFinish();
// Disable touch events, so that APZ knows not to wait for touch listeners.
// Also explicitly set the content response timeout, so we know how long it
// is (see comment in takeSnapshots).
// Finally, enable smooth scrolling, so that the wheel-scroll we do as part
// of the test triggers an APZ animation rather than doing an instant scroll.
// Note that this pref doesn't work for the synthesized wheel events on OS X,
// those are hard-coded to be instant scrolls.
pushPrefs([["dom.w3c_touch_events.enabled", 0],
["apz.content_response_timeout", kResponseTimeoutMs],
["general.smoothscroll", true]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
}
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,106 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
-->
<head>
<title>Test for Bug 1013412</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
#content {
height: 800px;
overflow: scroll;
}
#scroller {
height: 2000px;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
#scrollbox {
margin-top: 200px;
width: 500px;
height: 500px;
border-radius: 250px;
box-shadow: inset 0 0 0 60px #555;
background: #777;
}
#circle {
position: relative;
left: 240px;
top: 20px;
border: 10px solid white;
border-radius: 10px;
width: 0px;
height: 0px;
transform-origin: 10px 230px;
will-change: transform;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
<p id="display"></p>
<div id="content">
<p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
<div id="scroller">
<div id="scrollbox">
<div id="circle"></div>
</div>
</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
var rotation = 0;
var rotationAdjusted = false;
var incrementForMode = function (mode) {
switch (mode) {
case WheelEvent.DOM_DELTA_PIXEL: return 1;
case WheelEvent.DOM_DELTA_LINE: return 15;
case WheelEvent.DOM_DELTA_PAGE: return 400;
}
return 0;
};
document.getElementById("scrollbox").addEventListener("wheel", function (e) {
rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
rotationAdjusted = true;
e.preventDefault();
});
function* test(testDriver) {
var content = document.getElementById('content');
for (i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
yield synthesizeNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5, testDriver);
}
var scrollbox = document.getElementById('scrollbox');
is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
is(rotationAdjusted, true, "The rotation should have been adjusted");
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
// If we allow smooth scrolling the "smooth" scrolling may cause the page to
// glide past the scrollbox (which is supposed to stop the scrolling) and so
// we might end up at the bottom of the page.
pushPrefs([["general.smoothScroll", false]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
</script>
</pre>
</body>
</html>

View file

@ -0,0 +1,137 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
-->
<head>
<title>Test for Bug 1175585</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
<script type="application/javascript" src="apz_test_native_event_utils.js"></script>
<script type="application/javascript" src="apz_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
#outer-frame {
height: 500px;
overflow: scroll;
background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
}
#inner-frame {
margin-top: 25%;
height: 200%;
width: 75%;
overflow: scroll;
}
#inner-content {
height: 200%;
width: 200%;
background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175585">APZ wheel transactions test</a>
<p id="display"></p>
<div id="outer-frame">
<div id="inner-frame">
<div id="inner-content"></div>
</div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
function scrollWheelOver(element, deltaY, testDriver) {
synthesizeNativeWheelAndWaitForScrollEvent(element, 10, 10, 0, deltaY, testDriver);
}
function* test(testDriver) {
var outer = document.getElementById('outer-frame');
var inner = document.getElementById('inner-frame');
var innerContent = document.getElementById('inner-content');
// Register a wheel event listener that records the target of
// the last wheel event, so that we can make assertions about it.
var lastWheelTarget;
var wheelTargetRecorder = function(e) { lastWheelTarget = e.target; };
window.addEventListener("wheel", wheelTargetRecorder);
// Scroll |outer| to the bottom.
while (outer.scrollTop < outer.scrollTopMax) {
yield scrollWheelOver(outer, -10, testDriver);
}
// Verify that this has brought |inner| under the wheel.
is(lastWheelTarget, innerContent, "'inner-content' should have been brought under the wheel");
window.removeEventListener("wheel", wheelTargetRecorder);
// Immediately after, scroll it back up a bit.
yield scrollWheelOver(outer, 10, testDriver);
// Check that it was |outer| that scrolled back, and |inner| didn't
// scroll at all, as all the above scrolls should be in the same
// transaction.
ok(outer.scrollTop < outer.scrollTopMax, "'outer' should have scrolled back a bit");
is(inner.scrollTop, 0, "'inner' should not have scrolled");
// The next part of the test is related to the transaction timeout.
// Turn it down a bit so waiting for the timeout to elapse doesn't
// slow down the test harness too much.
var timeout = 5;
yield SpecialPowers.pushPrefEnv({"set": [["mousewheel.transaction.timeout", timeout]]}, testDriver);
SimpleTest.requestFlakyTimeout("we are testing code that measures actual elapsed time between two events");
// Scroll up a bit more. It's still |outer| scrolling because
// |inner| is still scrolled all the way to the top.
yield scrollWheelOver(outer, 10, testDriver);
// Wait for the transaction timeout to elapse.
// timeout * 5 is used to make it less likely that the timeout is less than
// the system timestamp resolution
yield window.setTimeout(testDriver, timeout * 5);
// Now scroll down. The transaction having timed out, the event
// should pick up a new target, and that should be |inner|.
yield scrollWheelOver(outer, -10, testDriver);
ok(inner.scrollTop > 0, "'inner' should have been scrolled");
// Finally, test scroll handoff after a timeout.
// Continue scrolling |inner| down to the bottom.
var prevScrollTop = inner.scrollTop;
while (inner.scrollTop < inner.scrollTopMax) {
yield scrollWheelOver(outer, -10, testDriver);
// Avoid a failure getting us into an infinite loop.
ok(inner.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
prevScrollTop = inner.scrollTop;
}
// Wait for the transaction timeout to elapse.
// timeout * 5 is used to make it less likely that the timeout is less than
// the system timestamp resolution
yield window.setTimeout(testDriver, timeout * 5);
// Continued downward scrolling should scroll |outer| to the bottom.
prevScrollTop = outer.scrollTop;
while (outer.scrollTop < outer.scrollTopMax) {
yield scrollWheelOver(outer, -10, testDriver);
// Avoid a failure getting us into an infinite loop.
ok(outer.scrollTop > prevScrollTop, "scrolling down should increase scrollTop");
prevScrollTop = outer.scrollTop;
}
}
SimpleTest.waitForExplicitFinish();
// Disable smooth scrolling because it makes the test flaky (we don't have a good
// way of detecting when the scrolling is finished).
pushPrefs([["general.smoothScroll", false]])
.then(waitUntilApzStable)
.then(runContinuation(test))
.then(SimpleTest.finish);
</script>
</pre>
</body>
</html>