Bug 496103: don't cancel mousedown events for menus (prevents dragging bookmark folders on the bookmark toolbar), r+sr=Neil. Test by Marco Bonardo <mak77@bonardo.net>, r=dietrich
authorGavin Sharp <gavin@mozilla.com>
Thu, 04 Jun 2009 16:52:25 -0400
changeset 25870 b35ad456cbf6c50475f6e43cf41707d4ed7b2356
parent 25869 5bb3a53e5ddda80bfeaca6d300103ce41ed5e3d6
child 25871 db15a52a4fd40a48c45cf6f9b831108c7c7599d2
push id1646
push usergsharp@mozilla.com
push dateThu, 04 Jun 2009 20:52:32 +0000
reviewersdietrich
bugs496103
milestone1.9.1pre
Bug 496103: don't cancel mousedown events for menus (prevents dragging bookmark folders on the bookmark toolbar), r+sr=Neil. Test by Marco Bonardo <mak77@bonardo.net>, r=dietrich
browser/components/places/tests/browser/Makefile.in
browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
layout/xul/base/src/nsMenuFrame.cpp
--- a/browser/components/places/tests/browser/Makefile.in
+++ b/browser/components/places/tests/browser/Makefile.in
@@ -53,12 +53,13 @@ include $(topsrcdir)/config/rules.mk
 	browser_sort_in_library.js \
 	browser_library_open_leak.js \
 	browser_library_panel_leak.js \
 	browser_library_search.js \
 	browser_history_sidebar_search.js \
 	browser_bookmarksProperties.js \
 	browser_forgetthissite_single.js \
 	browser_library_left_pane_commands.js \
+	browser_drag_bookmarks_on_toolbar.js \
 	$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
@@ -0,0 +1,264 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Marco Bonardo <mak77@bonardo.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const TEST_URL = "http://www.mozilla.org";
+const TEST_TITLE = "example_title";
+
+var gBookmarksToolbar = window.document.getElementById("bookmarksBarContent");
+var dragDirections = { LEFT: 0, UP: 1, RIGHT: 2, DOWN: 3 };
+
+/**
+ * Tests dragging on toolbar.
+ *
+ * We must test these 2 cases:
+ *   - Dragging toward left, top, right should start a drag.
+ *   - Dragging toward down should should open the container if the item is a
+ *     container, drag the item otherwise.
+ *
+ * @param aElement
+ *        DOM node element we will drag
+ * @param aExpectedDragData
+ *        Array of flavors and values in the form:
+ *        [ ["text/plain: sometext", "text/html: <b>sometext</b>"], [...] ]
+ *        Pass an empty array to check that drag even has been canceled.
+ * @param aDirection
+ *        Direction for the dragging gesture, see dragDirections helper object.
+ */
+function synthesizeDragWithDirection(aElement, aExpectedDragData, aDirection) {
+  var trapped = false;
+
+  // Dragstart listener function.
+  var trapDrag = function(event) {
+    trapped = true;
+    var dataTransfer = event.dataTransfer;
+    is(dataTransfer.mozItemCount, aExpectedDragData.length,
+       "Number of dragged items should be the same.");
+
+    for (var t = 0; t < dataTransfer.mozItemCount; t++) {
+      var types = dataTransfer.mozTypesAt(t);
+      var expecteditem = aExpectedDragData[t];
+      is(types.length, expecteditem.length,
+        "Number of flavors for item " + t + " should be the same.");
+
+      for (var f = 0; f < types.length; f++) {
+        is(types[f], expecteditem[f].substring(0, types[f].length),
+           "Flavor " + types[f] + " for item " + t + " should be the same.");
+        is(dataTransfer.mozGetDataAt(types[f], t),
+           expecteditem[f].substring(types[f].length + 2),
+           "Contents for item " + t + " with flavor " + types[f] + " should be the same.");
+      }
+    }
+
+    if (!aExpectedDragData.length)
+      ok(event.getPreventDefault(), "Drag has been canceled.");
+
+    event.preventDefault();
+    event.stopPropagation();
+  }
+
+  var xIncrement = 0;
+  var yIncrement = 0;
+
+  switch (aDirection) {
+    case dragDirections.LEFT:
+      xIncrement = -1;
+      break;
+    case dragDirections.RIGHT:
+      xIncrement = +1;
+      break;
+    case dragDirections.UP:
+      yIncrement = -1;
+      break;
+    case dragDirections.DOWN:
+      yIncrement = +1;
+      break;
+  }
+
+  var rect = aElement.getBoundingClientRect();
+  var startingPoint = { x: (rect.right - rect.left)/2,
+                        y: (rect.bottom - rect.top)/2 };
+
+  EventUtils.synthesizeMouse(aElement,
+                             startingPoint.x,
+                             startingPoint.y,
+                             { type: "mousedown" });
+  EventUtils.synthesizeMouse(aElement,
+                             startingPoint.x + xIncrement * 1,
+                             startingPoint.y + yIncrement * 1,
+                             { type: "mousemove" });
+  gBookmarksToolbar.addEventListener("dragstart", trapDrag, false);
+  EventUtils.synthesizeMouse(aElement,
+                             startingPoint.x + xIncrement * 8,
+                             startingPoint.y + yIncrement * 8,
+                             { type: "mousemove" });
+  ok(trapped, "A dragstart event has been trapped.");
+  gBookmarksToolbar.removeEventListener("dragstart", trapDrag, false);
+  EventUtils.synthesizeMouse(aElement,
+                             startingPoint.x + xIncrement * 8,
+                             startingPoint.y + yIncrement * 8,
+                             { type: "mouseup" });
+
+  // Cleanup eventually opened menus.
+  if (aElement.localName == "menu" && aElement.open)
+    aElement.open = false;
+}
+
+function getToolbarNodeForItemId(aItemId) {
+  var children = gBookmarksToolbar.childNodes;
+  var node = null;
+  for (var i = 0; i < children.length; i++) {
+    if (aItemId == children[i].node.itemId) {
+      node = children[i];
+      break;
+    }
+  }
+  return node;
+}
+
+function getExpectedDataForPlacesNode(aNode) {
+  var wrappedNode = [];
+  var flavors = ["text/x-moz-place",
+                 "text/x-moz-url",
+                 "text/plain",
+                 "text/html"];
+
+  flavors.forEach(function(aFlavor) {
+    var wrappedFlavor = aFlavor + ": " +
+                        PlacesUtils.wrapNode(aNode, aFlavor);
+    wrappedNode.push(wrappedFlavor);
+  });
+
+  return [wrappedNode];
+}
+
+var gTests = [
+
+//------------------------------------------------------------------------------
+
+  {
+    desc: "Drag a folder on toolbar",
+    run: function() {
+      // Create a test folder to be dragged.
+      var folderId = PlacesUtils.bookmarks
+                                .createFolder(PlacesUtils.toolbarFolderId,
+                                              TEST_TITLE,
+                                              PlacesUtils.bookmarks.DEFAULT_INDEX);
+      var element = getToolbarNodeForItemId(folderId);
+      isnot(element, null, "Found node on toolbar");
+
+      isnot(element.node, null, "Toolbar node has an associated Places node.");
+      var expectedData = getExpectedDataForPlacesNode(element.node);
+
+      ok(true, "Dragging left");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
+      ok(true, "Dragging right");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
+      ok(true, "Dragging up");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
+      ok(true, "Dragging down");
+      synthesizeDragWithDirection(element, new Array(), dragDirections.DOWN);
+
+      // Cleanup.
+      PlacesUtils.bookmarks.removeItem(folderId);
+    }
+  },
+
+//------------------------------------------------------------------------------
+
+  {
+    desc: "Drag a bookmark on toolbar",
+    run: function() {
+      //XXX bug 496266: this test causes a page navigation to the bookmark, which in
+      // turn causes a history visit to be added for the page, which messes up
+      // subsequent tests, so disable it for now
+      return;
+
+      // Create a test bookmark to be dragged.
+      var itemId = PlacesUtils.bookmarks
+                              .insertBookmark(PlacesUtils.toolbarFolderId,
+                                              PlacesUtils._uri(TEST_URL),
+                                              PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                              TEST_TITLE);
+      var element = getToolbarNodeForItemId(itemId);
+      isnot(element, null, "Found node on toolbar");
+
+      isnot(element.node, null, "Toolbar node has an associated Places node.");
+      var expectedData = getExpectedDataForPlacesNode(element.node);
+
+      ok(true, "Dragging left");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.LEFT);
+      ok(true, "Dragging right");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.RIGHT);
+      ok(true, "Dragging up");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.UP);
+      ok(true, "Dragging down");
+      synthesizeDragWithDirection(element, expectedData, dragDirections.DOWN);
+
+      // Cleanup.
+      PlacesUtils.bookmarks.removeItem(itemId);
+    }
+  },
+];
+
+function nextTest() {
+  if (gTests.length) {
+    var test = gTests.shift();
+    dump("TEST: " + test.desc + "\n");
+    ok(true, "TEST: " + test.desc);
+    test.run();
+
+    setTimeout(nextTest, 0);
+  }
+  else
+    finish();
+}
+
+function test() {
+  var osString = Cc["@mozilla.org/xre/app-info;1"].
+                 getService(Ci.nsIXULRuntime).OS;
+
+  // XXX bug 496277: this test fails on linux for some reason
+  if (osString == "Linux") {
+    finish();
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  nextTest();
+}
+
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -456,23 +456,23 @@ nsMenuFrame::HandleEvent(nsPresContext* 
 #endif
   }
   else if (aEvent->eventStructType == NS_MOUSE_EVENT &&
            aEvent->message == NS_MOUSE_BUTTON_DOWN &&
            static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton &&
            !IsDisabled() && IsMenu()) {
     // The menu item was selected. Bring up the menu.
     // We have children.
+    // Don't prevent the default action here, since that will also cancel
+    // potential drag starts.
     if (!mMenuParent || mMenuParent->IsMenuBar()) {
-      *aEventStatus = nsEventStatus_eConsumeNoDefault;
       ToggleMenuState();
     }
     else {
       if (!IsOpen()) {
-        *aEventStatus = nsEventStatus_eConsumeNoDefault;
         OpenMenu(PR_FALSE);
       }
     }
   }
   else if (
 #ifndef NSCONTEXTMENUISMOUSEUP
            (aEvent->eventStructType == NS_MOUSE_EVENT &&
             aEvent->message == NS_MOUSE_BUTTON_UP &&