Bug 1154140 - Remove stopPropagation call from ChromeUtils.synthesizeDrop. r=enndeakin
authorTooru Fujisawa <arai_a@mac.com>
Wed, 13 May 2015 18:58:59 +0900
changeset 243664 1c91f5d39dea877e84bf75f38d7772c8893c1933
parent 243663 7a62238aecdc491ae7cc60d37de1509d98e7a08e
child 243665 324c3423deafbf1db194df3d6a2accbad2b67c42
child 243718 7923a0c837b56efcd1376b95318fe4cdce03d5e8
push id28744
push userkwierso@gmail.com
push dateWed, 13 May 2015 18:12:16 +0000
treeherdermozilla-central@324c3423deaf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersenndeakin
bugs1154140
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1154140 - Remove stopPropagation call from ChromeUtils.synthesizeDrop. r=enndeakin
browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
browser/components/customizableui/test/head.js
testing/mochitest/tests/SimpleTest/ChromeUtils.js
--- a/browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
+++ b/browser/components/customizableui/test/browser_1003588_no_specials_in_panel.js
@@ -1,13 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+function simulateItemDragAndEnd(aToDrag, aTarget) {
+  var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
+           getService(Components.interfaces.nsIDragService);
+
+  ds.startDragSession();
+  try {
+    var [result, dataTransfer] = ChromeUtils.synthesizeDragOver(aToDrag.parentNode, aTarget);
+    ChromeUtils.synthesizeDropAfterDragOver(result, dataTransfer, aTarget);
+    // Send dragend to move dragging item back to initial place.
+    EventUtils.sendDragEvent({ type: "dragend", dataTransfer: dataTransfer },
+                             aToDrag.parentNode);
+  } finally {
+    ds.endDragSession(true);
+  }
+}
 
 add_task(function* checkNoAddingToPanel() {
   let area = CustomizableUI.AREA_PANEL;
   let previousPlacements = getAreaWidgetIds(area);
   CustomizableUI.addWidgetToArea("separator", area);
   CustomizableUI.addWidgetToArea("spring", area);
   CustomizableUI.addWidgetToArea("spacer", area);
   assertAreaPlacements(area, previousPlacements);
@@ -61,17 +76,17 @@ add_task(function* checkDragging() {
     if (CustomizableUI.isSpecialWidget(id)) {
       elementsToMove.push(id);
     }
   }
   is(elementsToMove.length, 3, "Should have 3 elements to try and drag.");
 
   yield startCustomizing();
   for (let id of elementsToMove) {
-    simulateItemDrag(document.getElementById(id), PanelUI.contents);
+    simulateItemDragAndEnd(document.getElementById(id), PanelUI.contents);
   }
 
   assertAreaPlacements(startArea, placementsWithSpecials);
   assertAreaPlacements(targetArea, startingTargetPlacements);
 
   for (let id of elementsToMove) {
     simulateItemDrag(document.getElementById(id), gCustomizeMode.visiblePalette);
   }
--- a/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
+++ b/browser/components/customizableui/test/browser_962069_drag_to_overflow_chevron.js
@@ -21,18 +21,30 @@ add_task(function*() {
   let panelShownPromise = promisePanelElementShown(window, widgetOverflowPanel);
   let identityBox = document.getElementById("identity-box");
   let overflowChevron = document.getElementById("nav-bar-overflow-button");
 
   // Listen for hiding immediately so we don't miss the event because of the
   // async-ness of the 'shown' yield...
   let panelHiddenPromise = promisePanelElementHidden(window, widgetOverflowPanel);
 
-  ChromeUtils.synthesizeDrop(identityBox, overflowChevron, [], null);
-  yield panelShownPromise;
+  var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
+           getService(Components.interfaces.nsIDragService);
+
+  ds.startDragSession();
+  try {
+    var [result, dataTransfer] = ChromeUtils.synthesizeDragOver(identityBox, overflowChevron);
+
+    // Wait for showing panel before ending drag session.
+    yield panelShownPromise;
+
+    ChromeUtils.synthesizeDropAfterDragOver(result, dataTransfer, overflowChevron);
+  } finally {
+    ds.endDragSession(true);
+  }
 
   info("Overflow panel is shown.");
 
   widgetOverflowPanel.hidePopup();
   yield panelHiddenPromise;
 });
 
 add_task(function*() {
--- a/browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
+++ b/browser/components/customizableui/test/browser_978084_dragEnd_after_move.js
@@ -16,29 +16,31 @@ add_task(function() {
   draggedItem.setAttribute("label", "Test");
   draggedItem.setAttribute("removable", "true");
   let navbar = document.getElementById("nav-bar");
   navbar.customizationTarget.appendChild(draggedItem);
   yield startCustomizing();
   simulateItemDrag(draggedItem, gCustomizeMode.visiblePalette);
   is(document.documentElement.hasAttribute("customizing-movingItem"), false,
      "Make sure customizing-movingItem is removed after dragging to the palette");
+  yield endCustomizing();
 });
 
 // Drop on a customization target itself
 add_task(function() {
   draggedItem = document.createElement("toolbarbutton");
   draggedItem.id = "test-dragEnd-after-move2";
   draggedItem.setAttribute("label", "Test");
   draggedItem.setAttribute("removable", "true");
   let dest = createToolbarWithPlacements("test-dragEnd");
   let navbar = document.getElementById("nav-bar");
   navbar.customizationTarget.appendChild(draggedItem);
   yield startCustomizing();
   simulateItemDrag(draggedItem, dest.customizationTarget);
   is(document.documentElement.hasAttribute("customizing-movingItem"), false,
      "Make sure customizing-movingItem is removed");
+  yield endCustomizing();
 });
 
 add_task(function asyncCleanup() {
   yield endCustomizing();
   yield resetCustomization();
 });
--- a/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
+++ b/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
@@ -13,16 +13,17 @@ add_task(function*() {
   let syncButton = document.getElementById("sync-button");
   ok(syncButton, "Sync button should exist.");
   is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should be a wrapper.");
 
   simulateItemDrag(syncButton, gNavToolbox.palette);
   ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
   ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
 
+  syncButton.scrollIntoView();
   simulateItemDrag(syncButton, toolbar);
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
 
   yield endCustomizing();
   isnot(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should not be a wrapper outside customize mode.");
   yield startCustomizing();
@@ -44,16 +45,17 @@ add_task(function*() {
   ok(CustomizableUI.inDefaultState, "Now that the toolbar is no longer registered, should be in default state.");
   ok(!gCustomizeMode.areas.has(toolbar), "Toolbar shouldn't be known to customize mode.");
 
   CustomizableUI.registerArea(TOOLBARID, {legacy: true, defaultPlacements: []});
   CustomizableUI.registerToolbarNode(toolbar, []);
   ok(!CustomizableUI.inDefaultState, "Now that the toolbar is registered again, should no longer be in default state.");
   ok(gCustomizeMode.areas.has(toolbar), "Toolbar should be known to customize mode again.");
 
+  syncButton.scrollIntoView();
   simulateItemDrag(syncButton, toolbar);
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
 
   let otherWin = yield openAndLoadWindow({}, true);
   let otherTB = otherWin.document.createElementNS(kNSXUL, "toolbar");
   otherTB.id = TOOLBARID;
@@ -73,16 +75,17 @@ add_task(function*() {
 
   ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
 
   simulateItemDrag(syncButton, gNavToolbox.palette);
   ok(!CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved to the palette");
   ok(gNavToolbox.palette.querySelector("#sync-button"), "Sync button really is in palette.");
   ok(!otherTB.querySelector("#sync-button"), "Sync button is in palette in other window, too.");
 
+  syncButton.scrollIntoView();
   simulateItemDrag(syncButton, toolbar);
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
   ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
 
   let wasInformedCorrectlyOfAreaDisappearing = false;
   //XXXgijs So we could be using promiseWindowClosed here. However, after
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -168,22 +168,18 @@ function todoAssertAreaPlacements(areaId
   todo(isPassing, "The area placements for " + areaId +
                   " should equal the expected placements.");
 }
 
 function getAreaWidgetIds(areaId) {
   return CustomizableUI.getWidgetIdsInArea(areaId);
 }
 
-function simulateItemDrag(toDrag, target) {
-  let docId = toDrag.ownerDocument.documentElement.id;
-  let dragData = [[{type: 'text/toolbarwrapper-id/' + docId,
-                    data: toDrag.id}]];
-  synthesizeDragStart(toDrag.parentNode, dragData);
-  synthesizeDrop(target, target, dragData);
+function simulateItemDrag(aToDrag, aTarget) {
+  synthesizeDrop(aToDrag.parentNode, aTarget);
 }
 
 function endCustomizing(aWindow=window) {
   if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
     return true;
   }
   Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
   let deferredEndCustomizing = Promise.defer();
--- a/testing/mochitest/tests/SimpleTest/ChromeUtils.js
+++ b/testing/mochitest/tests/SimpleTest/ChromeUtils.js
@@ -159,118 +159,193 @@ function synthesizeDragStart(element, ex
   x += step; y += step;
   EventUtils.synthesizeMouse(element, x, y, { type: "mousemove" }, aWindow);
   aWindow.removeEventListener("dragstart", trapDrag, false);
   EventUtils.synthesizeMouse(element, x, y, { type: "mouseup" }, aWindow);
   return result;
 }
 
 /**
- * Emulate a drop by emulating a dragstart and firing events dragenter, dragover, and drop.
- *  srcElement - the element to use to start the drag, usually the same as destElement
- *               but if destElement isn't suitable to start a drag on pass a suitable
- *               element for srcElement
- *  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}, ...], ... ]
- *               pass null to avoid modifying dataTransfer
- *  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
+ * INTERNAL USE ONLY
+ * Create an event object to pass to EventUtils.sendDragEvent.
+ *
+ * @param aType          The string represents drag event type.
+ * @param aDestElement   The element to fire the drag event, used to calculate
+ *                       screenX/Y and clientX/Y.
+ * @param aDestWindow    Optional; Defaults to the current window object.
+ * @param aDataTransfer  dataTransfer for current drag session.
+ * @param aDragEvent     The object contains properties to override the event
+ *                       object
+ * @return               An object to pass to EventUtils.sendDragEvent.
+ */
+function createDragEventObject(aType, aDestElement, aDestWindow, aDataTransfer,
+                               aDragEvent)
+{
+  var destRect = aDestElement.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;
+  }
+  return Object.assign({ type: aType,
+                         screenX: destScreenX, screenY: destScreenY,
+                         clientX: destClientX, clientY: destClientY,
+                         dataTransfer: aDataTransfer }, aDragEvent);
+}
+
+/**
+ * Emulate a event sequence of dragstart, dragenter, and dragover.
  *
- * Returns the drop effect that was desired.
+ * @param aSrcElement   The element to use to start the drag.
+ * @param aDestElement  The element to fire the dragover, dragenter events
+ * @param aDragData     The data to supply for the data transfer.
+ *                      This data is in the format:
+ *                        [ [ {type: value, data: value}, ...], ... ]
+ *                      Pass null to avoid modifying dataTransfer.
+ * @param aDropEffect   The drop effect to set during the dragstart event, or
+ *                      'move' if null.
+ * @param aWindow       Optional; Defaults to the current window object.
+ * @param aDestWindow   Optional; Defaults to aWindow.
+ *                      Used when aDestElement is in a different window than
+ *                      aSrcElement.
+ * @param aDragEvent    Optional; Defaults to empty object. Overwrites an object
+ *                      passed to EventUtils.sendDragEvent.
+ * @return              A two element array, where the first element is the
+ *                      value returned from EventUtils.sendDragEvent for
+ *                      dragover event, and the second element is the
+ *                      dataTransfer for the current drag session.
  */
-function synthesizeDrop(srcElement, destElement, dragData, dropEffect, aWindow, aDestWindow, aDragEvent={})
+function synthesizeDragOver(aSrcElement, aDestElement, aDragData, aDropEffect, aWindow, aDestWindow, aDragEvent={})
+{
+  if (!aWindow)
+    aWindow = window;
+  if (!aDestWindow)
+    aDestWindow = aWindow;
+
+  var dataTransfer;
+  var trapDrag = function(event) {
+    dataTransfer = event.dataTransfer;
+    if (aDragData) {
+      for (var i = 0; i < aDragData.length; i++) {
+        var item = aDragData[i];
+        for (var j = 0; j < item.length; j++) {
+          dataTransfer.mozSetDataAt(item[j].type, item[j].data, i);
+        }
+      }
+    }
+    dataTransfer.dropEffect = aDropEffect || "move";
+    event.preventDefault();
+  };
+
+  // need to use real mouse action
+  aWindow.addEventListener("dragstart", trapDrag, true);
+  EventUtils.synthesizeMouseAtCenter(aSrcElement, { type: "mousedown" }, aWindow);
+
+  var rect = aSrcElement.getBoundingClientRect();
+  var x = rect.width / 2;
+  var y = rect.height / 2;
+  EventUtils.synthesizeMouse(aSrcElement, x, y, { type: "mousemove" }, aWindow);
+  EventUtils.synthesizeMouse(aSrcElement, x+10, y+10, { type: "mousemove" }, aWindow);
+  aWindow.removeEventListener("dragstart", trapDrag, true);
+
+  var event = createDragEventObject("dragenter", aDestElement, aDestWindow,
+                                    dataTransfer, aDragEvent);
+  EventUtils.sendDragEvent(event, aDestElement, aDestWindow);
+
+  event = createDragEventObject("dragover", aDestElement, aDestWindow,
+                                dataTransfer, aDragEvent);
+  var result = EventUtils.sendDragEvent(event, aDestElement, aDestWindow);
+
+  return [result, dataTransfer];
+}
+
+/**
+ * Emulate the drop event and mouseup event.
+ * This should be called after synthesizeDragOver.
+ *
+ * @param aResult        The first element of the array returned from
+ *                       synthesizeDragOver.
+ * @param aDataTransfer  The second element of the array returned from
+ *                       synthesizeDragOver.
+ * @param aDestElement   The element to fire the drop event.
+ * @param aDestWindow    Optional; Defaults to the current window object.
+ * @param aDragEvent     Optional; Defaults to empty object. Overwrites an
+ *                       object passed to EventUtils.sendDragEvent.
+ * @return               "none" if aResult is true,
+ *                       aDataTransfer.dropEffect otherwise.
+ */
+function synthesizeDropAfterDragOver(aResult, aDataTransfer, aDestElement, aDestWindow, aDragEvent={})
+{
+  if (!aDestWindow)
+    aDestWindow = window;
+
+  var effect = aDataTransfer.dropEffect;
+  var event;
+
+  if (aResult) {
+    effect = "none";
+  } else if (effect != "none") {
+    event = createDragEventObject("drop", aDestElement, aDestWindow,
+                                  aDataTransfer, aDragEvent);
+    EventUtils.sendDragEvent(event, aDestElement, aDestWindow);
+  }
+
+  EventUtils.synthesizeMouseAtCenter(aDestElement, { type: "mouseup" }, aDestWindow);
+
+  return effect;
+}
+
+/**
+ * Emulate a drag and drop by emulating a dragstart and firing events dragenter,
+ * dragover, and drop.
+ *
+ * @param aSrcElement   The element to use to start the drag.
+ * @param aDestElement  The element to fire the dragover, dragenter events
+ * @param aDragData     The data to supply for the data transfer.
+ *                      This data is in the format:
+ *                        [ [ {type: value, data: value}, ...], ... ]
+ *                      Pass null to avoid modifying dataTransfer.
+ * @param aDropEffect   The drop effect to set during the dragstart event, or
+ *                      'move' if null.
+ * @param aWindow       Optional; Defaults to the current window object.
+ * @param aDestWindow   Optional; Defaults to aWindow.
+ *                      Used when aDestElement is in a different window than
+ *                      aSrcElement.
+ * @param aDragEvent    Optional; Defaults to empty object. Overwrites an object
+ *                      passed to EventUtils.sendDragEvent.
+ * @return              The drop effect that was desired.
+ */
+function synthesizeDrop(aSrcElement, aDestElement, aDragData, aDropEffect, aWindow, aDestWindow, aDragEvent={})
 {
   if (!aWindow)
     aWindow = window;
   if (!aDestWindow)
     aDestWindow = aWindow;
 
   var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
            getService(Components.interfaces.nsIDragService);
 
-  var dataTransfer;
-  var trapDrag = function(event) {
-    dataTransfer = event.dataTransfer;
-    if (dragData) {
-      for (var i = 0; i < dragData.length; i++) {
-        var item = dragData[i];
-        for (var j = 0; j < item.length; j++) {
-          dataTransfer.mozSetDataAt(item[j].type, item[j].data, i);
-        }
-      }
-    }
-    dataTransfer.dropEffect = dropEffect || "move";
-    event.preventDefault();
-    if (dragData) {
-      event.stopPropagation();
-    }
-  }
-
   ds.startDragSession();
 
   try {
-    // need to use real mouse action
-    aWindow.addEventListener("dragstart", trapDrag, true);
-    EventUtils.synthesizeMouseAtCenter(srcElement, { type: "mousedown" }, aWindow);
-
-    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);
-
-    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 = 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;
+    var [result, dataTransfer] = synthesizeDragOver(aSrcElement, aDestElement,
+                                                    aDragData, aDropEffect,
+                                                    aWindow, aDestWindow,
+                                                    aDragEvent);
+    return synthesizeDropAfterDragOver(result, dataTransfer, aDestElement,
+                                       aDestWindow, aDragEvent);
   } finally {
     ds.endDragSession(true);
   }
-};
+}
 
 var PluginUtils =
 {
   withTestPlugin : function(callback)
   {
     if (typeof Components == "undefined")
     {
       todo(false, "Not a Mozilla-based browser");