Bug 1147700 - Part 1: Add aDragEvent parameter to ChromeUtils.synthesizeDrop to modify DragEvent, and set default position to center of destElement. r=enndeakin
authorTooru Fujisawa <arai_a@mac.com>
Tue, 14 Apr 2015 13:07:57 +0900
changeset 257873 697925b7e02446767dc7e615f40469538d9ac13e
parent 257872 c80858490720a04e73d94b878f109f08114253aa
child 257874 fa7e6ceab5f372a2a6a30400d8394992b2b091e0
push id8007
push userraliiev@mozilla.com
push dateMon, 11 May 2015 19:23:16 +0000
treeherdermozilla-aurora@e2ce1aac996e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersenndeakin
bugs1147700
milestone40.0a1
Bug 1147700 - Part 1: Add aDragEvent parameter to ChromeUtils.synthesizeDrop to modify DragEvent, and set default position to center of destElement. r=enndeakin
browser/base/content/test/general/browser_tabDrop.js
testing/mochitest/tests/SimpleTest/ChromeUtils.js
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/browser/base/content/test/general/browser_tabDrop.js
+++ b/browser/base/content/test/general/browser_tabDrop.js
@@ -47,20 +47,26 @@ function test() {
   function drop(text, valid) {
     triggeredDropCount++;
     if (valid)
       validDropCount++;
     executeSoon(function () {
       // A drop type of "link" onto an existing tab would normally trigger a
       // load in that same tab, but tabbrowser code in _getDragTargetTab treats
       // drops on the outer edges of a tab differently (loading a new tab
-      // instead). The events created by synthesizeDrop have all of their
+      // instead). Make events created by synthesizeDrop have all of their
       // coordinates set to 0 (screenX/screenY), so they're treated as drops
       // on the outer edge of the tab, thus they open new tabs.
-      ChromeUtils.synthesizeDrop(newTab, newTab, [[{type: "text/plain", data: text}]], "link", window);
+      var event = {
+        clientX: 0,
+        clientY: 0,
+        screenX: 0,
+        screenY: 0,
+      };
+      ChromeUtils.synthesizeDrop(newTab, newTab, [[{type: "text/plain", data: text}]], "link", window, undefined, event);
     });
   }
 
   // Begin and end with valid drops to make sure we wait for all drops before
   // ending the test
   drop("mochi.test/first", true);
   drop("javascript:'bad'");
   drop("jAvascript:'bad'");
--- a/testing/mochitest/tests/SimpleTest/ChromeUtils.js
+++ b/testing/mochitest/tests/SimpleTest/ChromeUtils.js
@@ -171,28 +171,28 @@ function synthesizeDragStart(element, ex
  *  destElement - the element to fire the dragover, dragleave and drop events
  *  dragData - the data to supply for the data transfer
  *                     This data is in the format:
  *                       [ [ {type: value, data: value}, ...], ... ]
  *  dropEffect - the drop effect to set during the dragstart event, or 'move' if null
  *  aWindow - optional; defaults to the current window object.
  *  aDestWindow - optional; defaults to aWindow.
  *                Used when destElement is in a different window than srcElement.
+ *  aDragEvent - optional; defaults to empty object.
+ *                overwrite a event object passed to EventUtils.sendDragEvent
  *
  * Returns the drop effect that was desired.
  */
-function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, aDestWindow)
+function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, aDestWindow, aDragEvent={})
 {
   if (!aWindow)
     aWindow = window;
   if (!aDestWindow)
     aDestWindow = aWindow;
 
-  var gWindowUtils = aDestWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                                 getInterface(Components.interfaces.nsIDOMWindowUtils);
   var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
            getService(Components.interfaces.nsIDragService);
 
   var dataTransfer;
   var trapDrag = function(event) {
     dataTransfer = event.dataTransfer;
     for (var i = 0; i < dragData.length; i++) {
       var item = dragData[i];
@@ -214,30 +214,49 @@ function synthesizeDrop(srcElement, dest
 
     var rect = srcElement.getBoundingClientRect();
     var x = rect.width / 2;
     var y = rect.height / 2;
     EventUtils.synthesizeMouse(srcElement, x, y, { type: "mousemove" }, aWindow);
     EventUtils.synthesizeMouse(srcElement, x+10, y+10, { type: "mousemove" }, aWindow);
     aWindow.removeEventListener("dragstart", trapDrag, true);
 
-    event = aDestWindow.document.createEvent("DragEvents");
-    event.initDragEvent("dragenter", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
-    gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true);
-    var event = aDestWindow.document.createEvent("DragEvents");
-    event.initDragEvent("dragover", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
-    if (gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true)) {
+    var destRect = destElement.getBoundingClientRect();
+    var destClientX = destRect.left + destRect.width / 2;
+    var destClientY = destRect.top + destRect.height / 2;
+    var destScreenX = aDestWindow.mozInnerScreenX + destClientX;
+    var destScreenY = aDestWindow.mozInnerScreenY + destClientY;
+    if ("clientX" in aDragEvent && !("screenX" in aDragEvent)) {
+      aDragEvent.screenX = aDestWindow.mozInnerScreenX + aDragEvent.clientX;
+    }
+    if ("clientY" in aDragEvent && !("screenY" in aDragEvent)) {
+      aDragEvent.screenY = aDestWindow.mozInnerScreenY + aDragEvent.clientY;
+    }
+
+    var event = Object.assign({ type: "dragenter",
+                                screenX: destScreenX, screenY: destScreenY,
+                                clientX: destClientX, clientY: destClientY,
+                                dataTransfer: dataTransfer }, aDragEvent);
+    EventUtils.sendDragEvent(event, destElement, aDestWindow);
+
+    event = Object.assign({ type: "dragover",
+                            screenX: destScreenX, screenY: destScreenY,
+                            clientX: destClientX, clientY: destClientY,
+                            dataTransfer: dataTransfer }, aDragEvent);
+    if (EventUtils.sendDragEvent(event, destElement, aDestWindow)) {
       EventUtils.synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aDestWindow);
       return "none";
     }
 
     if (dataTransfer.dropEffect != "none") {
-      event = aDestWindow.document.createEvent("DragEvents");
-      event.initDragEvent("drop", true, true, aDestWindow, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
-      gWindowUtils.dispatchDOMEventViaPresShell(destElement, event, true);
+      event = Object.assign({ type: "drop",
+                              screenX: destScreenX, screenY: destScreenY,
+                              clientX: destClientX, clientY: destClientY,
+                              dataTransfer: dataTransfer }, aDragEvent);
+      EventUtils.sendDragEvent(event, destElement, aDestWindow);
     }
 
     EventUtils.synthesizeMouseAtCenter(destElement, { type: "mouseup" }, aDestWindow);
 
     return dataTransfer.dropEffect;
   } finally {
     ds.endDragSession(true);
   }
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -1,12 +1,13 @@
 /**
  * EventUtils provides some utility methods for creating and sending DOM events.
  * Current methods:
  *  sendMouseEvent
+ *  sendDragEvent
  *  sendChar
  *  sendString
  *  sendKey
  *  sendWheelAndPaint
  *  synthesizeMouse
  *  synthesizeMouseAtCenter
  *  synthesizePointer
  *  synthesizeWheel
@@ -89,16 +90,59 @@ function sendMouseEvent(aEvent, aTarget,
                        screenXArg, screenYArg, clientXArg, clientYArg,
                        ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
                        buttonArg, relatedTargetArg);
 
   return SpecialPowers.dispatchEvent(aWindow, aTarget, event);
 }
 
 /**
+ * Send a drag event to the node aTarget (aTarget can be an id, or an
+ * actual node) . The "event" passed in to aEvent is just a JavaScript
+ * object with the properties set that the real drag event object should
+ * have. This includes the type of the drag event.
+ */
+function sendDragEvent(aEvent, aTarget, aWindow=window) {
+  if (['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop'].indexOf(aEvent.type) == -1) {
+    throw new Error("sendDragEvent doesn't know about event type '" + aEvent.type + "'");
+  }
+
+  if (typeof aTarget == "string") {
+    aTarget = aWindow.document.getElementById(aTarget);
+  }
+
+  var event = aWindow.document.createEvent('DragEvent');
+
+  var typeArg          = aEvent.type;
+  var canBubbleArg     = true;
+  var cancelableArg    = true;
+  var viewArg          = aWindow;
+  var detailArg        = aEvent.detail        || 0;
+  var screenXArg       = aEvent.screenX       || 0;
+  var screenYArg       = aEvent.screenY       || 0;
+  var clientXArg       = aEvent.clientX       || 0;
+  var clientYArg       = aEvent.clientY       || 0;
+  var ctrlKeyArg       = aEvent.ctrlKey       || false;
+  var altKeyArg        = aEvent.altKey        || false;
+  var shiftKeyArg      = aEvent.shiftKey      || false;
+  var metaKeyArg       = aEvent.metaKey       || false;
+  var buttonArg        = aEvent.button        || 0;
+  var relatedTargetArg = aEvent.relatedTarget || null;
+  var dataTransfer     = aEvent.dataTransfer  || null;
+
+  event.initDragEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
+                      screenXArg, screenYArg, clientXArg, clientYArg,
+                      ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
+                      buttonArg, relatedTargetArg, dataTransfer);
+
+  var utils = _getDOMWindowUtils(aWindow);
+  return utils.dispatchDOMEventViaPresShell(aTarget, event, true);
+}
+
+/**
  * Send the char aChar to the focused element.  This method handles casing of
  * chars (sends the right charcode, and sends a shift key for uppercase chars).
  * No other modifiers are handled at this point.
  *
  * For now this method only works for ASCII characters and emulates the shift
  * key state on US keyboard layout.
  */
 function sendChar(aChar, aWindow) {