Bug 1385476 - Synthesize dblclick MouseEvent when performing webdriver actions; r=maja_zf
MozReview-Commit-ID: 9u2mtolhjUq
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -1204,16 +1204,20 @@ function dispatchPointerDown(a, inputSta
// Append a copy of |a| with pointerUp subtype
let copy = Object.assign({}, a, {subtype: action.PointerUp});
action.inputsToCancel.push(copy);
switch (inputState.subtype) {
case action.PointerType.Mouse:
let mouseEvent = new action.Mouse("mousedown", a.button);
mouseEvent.update(inputState);
+ if (event.DoubleClickTracker.isClicked()) {
+ mouseEvent = Object.assign({},
+ mouseEvent, {clickCount: 2});
+ }
event.synthesizeMouseAtPoint(
inputState.x,
inputState.y,
mouseEvent,
window);
if (event.MouseButton.isSecondary(a.button)) {
let contextMenuEvent = Object.assign({},
mouseEvent, {type: "contextmenu"});
@@ -1259,16 +1263,20 @@ function dispatchPointerUp(a, inputState
}
inputState.release(a.button);
switch (inputState.subtype) {
case action.PointerType.Mouse:
let mouseEvent = new action.Mouse("mouseup", a.button);
mouseEvent.update(inputState);
+ if (event.DoubleClickTracker.isClicked()) {
+ mouseEvent = Object.assign({},
+ mouseEvent, {clickCount: 2});
+ }
event.synthesizeMouseAtPoint(
inputState.x, inputState.y, mouseEvent, window);
break;
case action.PointerType.Pen:
case action.PointerType.Touch:
throw new UnsupportedOperationError("Only 'mouse' pointer type is supported");
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -9,16 +9,21 @@ this.event = {};
/* global content, is */
const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
Cu.import("chrome://marionette/content/element.js");
const {ElementNotInteractableError} =
Cu.import("chrome://marionette/content/error.js", {});
+const dblclickTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+// Max interval between two clicks that should result in a dblclick (in ms)
+const DBLCLICK_INTERVAL = 640;
+
this.EXPORTED_SYMBOLS = ["event"];
// TODO(ato): Document!
let seenEvent = false;
function getDOMWindowUtils(win) {
if (!win) {
win = window;
@@ -55,16 +60,39 @@ event.MouseButton = {
isAuxiliary(button) {
return button === 1;
},
isSecondary(button) {
return button === 2;
},
};
+event.DoubleClickTracker = {
+ firstClick: false,
+ isClicked() {
+ return event.DoubleClickTracker.firstClick;
+ },
+ setClick() {
+ if (!event.DoubleClickTracker.firstClick) {
+ event.DoubleClickTracker.firstClick = true;
+ event.DoubleClickTracker.startTimer();
+ }
+ },
+ resetClick() {
+ event.DoubleClickTracker.firstClick = false;
+ },
+ startTimer() {
+ dblclickTimer.initWithCallback(event.DoubleClickTracker.resetClick,
+ DBLCLICK_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
+ },
+ cancelTimer() {
+ dblclickTimer.cancel();
+ },
+};
+
/**
* Sends a mouse event to given target.
*
* @param {nsIDOMMouseEvent} mouseEvent
* Event to send.
* @param {(DOMElement|string)} target
* Target of event. Can either be an element or the ID of an element.
* @param {Window=} window
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -48,16 +48,24 @@ Cu.import("chrome://marionette/content/p
Cu.import("chrome://marionette/content/session.js");
Cu.importGlobalProperties(["URL"]);
let listenerId = null; // unique ID of this listener
let curContainer = {frame: content, shadowRoot: null};
let previousContainer = null;
+
+// Listen for click event to indicate one click has happened, so actions
+// code can send dblclick event, also resetClick and cancelTimer
+// after dblclick has happened.
+addEventListener("click", event.DoubleClickTracker.setClick);
+addEventListener("dblclick", event.DoubleClickTracker.resetClick);
+addEventListener("dblclick", event.DoubleClickTracker.cancelTimer);
+
const seenEls = new element.Store();
const SUPPORTED_STRATEGIES = new Set([
element.Strategy.ClassName,
element.Strategy.Selector,
element.Strategy.ID,
element.Strategy.Name,
element.Strategy.LinkText,
element.Strategy.PartialLinkText,
@@ -147,17 +155,16 @@ const loadListener = {
// The frame script got reloaded due to a new content process.
// Due to the time it takes to re-register the browser in Marionette,
// it can happen that page load events are missed before the listeners
// are getting attached again. By checking the document readyState the
// command can return immediately if the page load is already done.
let readyState = content.document.readyState;
let documentURI = content.document.documentURI;
logger.debug(`Check readyState "${readyState} for "${documentURI}"`);
-
// If the page load has already finished, don't setup listeners and
// timers but return immediatelly.
if (this.handleReadyState(readyState, documentURI)) {
return;
}
addEventListener("DOMContentLoaded", loadListener, true);
addEventListener("pageshow", loadListener, true);