Bug 1385476 - Synthesize dblclick MouseEvent when performing webdriver actions; r=maja_zf draft
authormuthuraj90ec@gmail.com
Fri, 24 Nov 2017 15:53:57 -0500
changeset 705211 76b2e3fbd3cbb86f062227c779e88e61fefc1d8d
parent 705036 3f6b9aaed8cd57954e0c960cde06d25228196456
child 705212 bb08fd1d94faf2e48bf53d153b06ac219ead8302
push id91400
push userbmo:mjzffr@gmail.com
push dateWed, 29 Nov 2017 16:45:18 +0000
reviewersmaja_zf
bugs1385476
milestone59.0a1
Bug 1385476 - Synthesize dblclick MouseEvent when performing webdriver actions; r=maja_zf MozReview-Commit-ID: 9u2mtolhjUq
testing/marionette/action.js
testing/marionette/event.js
testing/marionette/listener.js
--- 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);