Bug 1276107 - Avoid the footgun where, on Windows, when synthesizing a mouse move event, if the mouse is already at the target location the event is never dispatched. r=kats

MozReview-Commit-ID: 9hCJ3wpkOah

--HG--
extra : rebase_source : 92aa6b5b489fa8c3d1aa1ec7d2b979aa6af83a45
extra : amend_source : 22527c9c0222c923363681e46c09c201523e2c5e
extra : source : 5fddc3a80999cd6e284afbcabcddd74f398a06b9
This commit is contained in:
Botond Ballo 2016-06-03 13:12:36 -04:00
parent e9a73ef16a
commit 2e8d4b9af9
3 changed files with 40 additions and 9 deletions

View File

@ -175,12 +175,46 @@ function synthesizeNativeMouseMove(aElement, aX, aY) {
return true;
}
function equalPoints(aPt1, aPt2) {
if (!aPt1 || !aPt2) {
return false;
}
return aPt1.x == aPt2.x &&
aPt1.y == aPt2.y;
}
// 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) {
// Initialize, if necessary, a place to a store state that will persist
// across calls to this function.
var func = synthesizeNativeMouseMoveAndWaitForMoveEvent; // just so it's shorter
if (typeof func.persistentState == 'undefined') {
// If we're in a subtest, the test driver provides a variable where
// we can store state that persists across subtests. Otherwise, just
// create a variable that lives as long as this function.
if (typeof window.statePersistentAcrossSubtests == 'undefined') {
func.persistentState = {}
} else {
func.persistentState = window.statePersistentAcrossSubtests;
}
}
// On Windows, if the mouse is already at the target location, the mouse
// move event never gets dispatched. This is a significant potential footgun
// (if we try waiting for it when it'll never come); to avoid it, we store
// the last location we moved the mouse to, and just call the callback
// right away if the new location is the same.
var pt = coordinatesRelativeToWindow(aX, aY, aElement);
if (equalPoints(func.persistentState.lastMouseMoveLocation, pt)) {
setTimeout(aCallback, 0);
return true;
}
func.persistentState.lastMouseMoveLocation = pt;
var targetWindow = aElement.ownerDocument.defaultView;
targetWindow.addEventListener("mousemove", function mousemoveWaiter(e) {
targetWindow.removeEventListener("mousemove", mousemoveWaiter);
@ -250,11 +284,6 @@ function synthesizeNativeClick(aElement, aX, aY, aObserver = null) {
// 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.
// XXX FOOTGUN: On Windows, if the mouse cursor is already at the target
// position, the mouse-move event doesn't get dispatched, and
// we end up hanging waiting for it. As a result, care must
// be taken that the mouse isn't already at the target position
// when using this function.
function moveMouseAndScrollWheelOver(element, dx, dy, testDriver) {
return synthesizeNativeMouseMoveAndWaitForMoveEvent(element, dx, dy, function() {
synthesizeNativeWheelAndWaitForScrollEvent(element, dx, dy, 0, -10, testDriver);

View File

@ -148,6 +148,10 @@ function runSubtestsSeriallyInFreshWindows(aSubtests) {
var testIndex = -1;
var w = null;
// Some state that persists across subtests. This is made available to
// subtests to put things into / read things out of.
var statePersistentAcrossSubtests = {};
function advanceSubtestExecution() {
var test = aSubtests[testIndex];
if (w) {
@ -189,6 +193,7 @@ function runSubtestsSeriallyInFreshWindows(aSubtests) {
w = window.open('', "_blank");
w.subtestDone = advanceSubtestExecution;
w.SimpleTest = SimpleTest;
w.statePersistentAcrossSubtests = statePersistentAcrossSubtests;
w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;

View File

@ -30,10 +30,7 @@ function* test(testDriver) {
flushApzRepaints(testDriver);
});
scrollPos = fpos.scrollTop;
// The mouse is already at the right position. If we call moveMouseAndScrollWheelOver it
// hangs on windows waiting for the mouse-move, so instead we just synthesize
// the wheel directly.
yield synthesizeNativeWheelAndWaitForScrollEvent(fpos, 50, 150, 0, -10, testDriver);
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