Bug 1094818 - Use Bookmarks.jsm in controller.js. r=standard8
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 18 Jul 2017 19:18:02 +0200
changeset 372250 e72d0fc07a0bb274bd525f65c14bfc5ea7eae9f8
parent 372249 3d5283aa17337d157dac8c52d33fccd448d211ba
child 372251 409b4345b85aba3bd93b83c6f9a6a194a2d5073a
push id32273
push userkwierso@gmail.com
push dateWed, 02 Aug 2017 22:48:18 +0000
treeherdermozilla-central@4c7317211990 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersstandard8
bugs1094818
milestone56.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 1094818 - Use Bookmarks.jsm in controller.js. r=standard8 MozReview-Commit-ID: 7vli02K19bt
browser/base/content/browser-places.js
browser/components/places/content/bookmarkProperties.js
browser/components/places/content/browserPlacesViews.js
browser/components/places/content/controller.js
browser/components/places/content/editBookmarkOverlay.js
browser/components/places/content/menu.xml
browser/components/places/content/tree.xml
browser/components/places/content/treeView.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -592,17 +592,17 @@ var PlacesCommandHook = {
    */
   bookmarkCurrentPage: function PCH_bookmarkCurrentPage(aShowEditUI, aParent) {
     this.bookmarkPage(gBrowser.selectedBrowser, aParent, aShowEditUI)
         .catch(Components.utils.reportError);
   },
 
   /**
    * Adds a bookmark to the page targeted by a link.
-   * @param aParent
+   * @param aParentId
    *        The folder in which to create a new bookmark if aURL isn't
    *        bookmarked.
    * @param aURL (string)
    *        the address of the link target
    * @param aTitle
    *        The link text
    * @param [optional] aDescription
    *        The linked page description, if available
@@ -611,19 +611,23 @@ var PlacesCommandHook = {
     let node = await PlacesUIUtils.fetchNodeLike({ url: aURL });
     if (node) {
       PlacesUIUtils.showBookmarkDialog({ action: "edit",
                                          node
                                        }, window.top);
       return;
     }
 
+    let parentGuid = aParentId == PlacesUtils.bookmarksMenuFolderId ?
+                       PlacesUtils.bookmarks.menuGuid :
+                       await PlacesUtils.promiseItemGuid(aParentId);
     let ip = new InsertionPoint(aParentId,
                                 PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                Components.interfaces.nsITreeView.DROP_ON);
+                                Components.interfaces.nsITreeView.DROP_ON,
+                                null, null, parentGuid);
     PlacesUIUtils.showBookmarkDialog({ action: "add",
                                        type: "bookmark",
                                        uri: makeURI(aURL),
                                        title: aTitle,
                                        description: aDescription,
                                        defaultInsertionPoint: ip,
                                        hiddenRows: [ "description",
                                                      "location",
@@ -691,17 +695,18 @@ var PlacesCommandHook = {
    * @title     title
    *            The title of the feed. Optional.
    * @subtitle  subtitle
    *            A short description of the feed. Optional.
    */
   async addLiveBookmark(url, feedTitle, feedSubtitle) {
     let toolbarIP = new InsertionPoint(PlacesUtils.toolbarFolderId,
                                        PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                       Components.interfaces.nsITreeView.DROP_ON);
+                                       Components.interfaces.nsITreeView.DROP_ON,
+                                       null, null, PlacesUtils.bookmarks.toolbarGuid);
 
     let feedURI = makeURI(url);
     let title = feedTitle || gBrowser.contentTitle;
     let description = feedSubtitle;
     if (!description) {
       description = (await this._getPageDetails(gBrowser.selectedBrowser)).description;
     }
 
@@ -1107,33 +1112,35 @@ var PlacesMenuDNDHandler = {
   /**
    * Called when the user drags over the <menu> element.
    * @param   event
    *          The DragOver event.
    */
   onDragOver: function PMDH_onDragOver(event) {
     let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
                                 PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                Components.interfaces.nsITreeView.DROP_ON);
+                                Components.interfaces.nsITreeView.DROP_ON,
+                                null, null, PlacesUtils.bookmarks.menuGuid);
     if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer))
       event.preventDefault();
 
     event.stopPropagation();
   },
 
   /**
    * Called when the user drops on the <menu> element.
    * @param   event
    *          The Drop event.
    */
   onDrop: function PMDH_onDrop(event) {
     // Put the item at the end of bookmark menu.
     let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
                                 PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                Components.interfaces.nsITreeView.DROP_ON);
+                                Components.interfaces.nsITreeView.DROP_ON,
+                                null, null, PlacesUtils.bookmarks.menuGuid);
     PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
     PlacesControllerDragHelper.currentDropTarget = null;
     event.stopPropagation();
   }
 };
 
 /**
  * This object handles the initialization and uninitialization of the bookmarks
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -161,17 +161,18 @@ var BookmarkPropertiesPanel = {
         this._title = dialogInfo.title;
 
       if ("defaultInsertionPoint" in dialogInfo) {
         this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
       } else {
         this._defaultInsertionPoint =
           new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
                              PlacesUtils.bookmarks.DEFAULT_INDEX,
-                             Ci.nsITreeView.DROP_ON);
+                             Ci.nsITreeView.DROP_ON, null, null,
+                             PlacesUtils.bookmarks.menuGuid);
       }
 
       switch (dialogInfo.type) {
         case "bookmark":
           this._itemType = BOOKMARK_ITEM;
           if ("uri" in dialogInfo) {
             NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI,
                       "uri property should be a uri object");
@@ -488,21 +489,22 @@ var BookmarkPropertiesPanel = {
 
   /**
    * [New Item Mode] Get the insertion point details for the new item, given
    * dialog state and opening arguments.
    *
    * The container-identifier and insertion-index are returned separately in
    * the form of [containerIdentifier, insertionIndex]
    */
-  _getInsertionPointDetails: function BPP__getInsertionPointDetails() {
-    var containerId = this._defaultInsertionPoint.itemId;
-    var indexInContainer = this._defaultInsertionPoint.index;
-
-    return [containerId, indexInContainer];
+  async _getInsertionPointDetails() {
+    return [
+      this._defaultInsertionPoint.itemId,
+      await this._defaultInsertionPoint.getIndex(),
+      await this._defaultInsertionPoint.promiseGuid(),
+    ]
   },
 
   /**
    * Returns a transaction for creating a new bookmark item representing the
    * various fields and opening arguments of the dialog.
    */
   _getCreateNewBookmarkTransaction:
   function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) {
@@ -580,17 +582,17 @@ var BookmarkPropertiesPanel = {
       annotations.push(this._getDescriptionAnnotation(this._description));
 
     return new PlacesCreateFolderTransaction(this._title, aContainer,
                                              aIndex, annotations,
                                              childItemsTransactions);
   },
 
   async _createNewItem() {
-    let [container, index] = this._getInsertionPointDetails();
+    let [container, index] = await this._getInsertionPointDetails();
     let txn;
     switch (this._itemType) {
       case BOOKMARK_FOLDER:
         txn = this._getCreateNewFolderTransaction(container, index);
         break;
       case LIVEMARK_CONTAINER:
         txn = new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI,
                                                   this._title, container, index);
@@ -627,18 +629,17 @@ var BookmarkPropertiesPanel = {
       }
     });
   },
 
   async _promiseNewItem() {
     if (!PlacesUIUtils.useAsyncTransactions)
       return this._createNewItem();
 
-    let [containerId, index] = this._getInsertionPointDetails();
-    let parentGuid = await PlacesUtils.promiseItemGuid(containerId);
+    let [containerId, index, parentGuid] = await this._getInsertionPointDetails();
     let annotations = [];
     if (this._description) {
       annotations.push({ name: PlacesUIUtils.DESCRIPTION_ANNO,
                          value: this._description });
     }
     if (this._loadInSidebar) {
       annotations.push({ name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
                          value: true });
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -214,17 +214,18 @@ PlacesViewBase.prototype = {
         }
       }
     }
 
     if (PlacesControllerDragHelper.disallowInsertion(container))
       return null;
 
     return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
-                              index, orientation, tagName);
+                              index, orientation, tagName, null,
+                              PlacesUtils.getConcreteItemGuid(container));
   },
 
   buildContextMenu: function PVB_buildContextMenu(aPopup) {
     this._contextMenuShown = aPopup;
     window.updateCommands("places");
     return this.controller.buildContextMenu(aPopup);
   },
 
@@ -1418,68 +1419,73 @@ PlacesToolbar.prototype = {
         // If we are in the middle of it, drop inside it.
         // Otherwise, drop before it, with regards to RTL mode.
         let threshold = eltRect.width * 0.25;
         if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
           // Drop before this folder.
           dropPoint.ip =
             new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
-                               eltIndex, Ci.nsITreeView.DROP_BEFORE);
+                               eltIndex, Ci.nsITreeView.DROP_BEFORE, null, null,
+                               PlacesUtils.getConcreteItemGuid(this._resultNode));
           dropPoint.beforeIndex = eltIndex;
         } else if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
                             : (aEvent.clientX < eltRect.right - threshold)) {
           // Drop inside this folder.
           let tagName = PlacesUtils.nodeIsTagQuery(elt._placesNode) ?
                         elt._placesNode.title : null;
           dropPoint.ip =
             new InsertionPoint(PlacesUtils.getConcreteItemId(elt._placesNode),
-                               -1, Ci.nsITreeView.DROP_ON,
-                               tagName);
+                               -1, Ci.nsITreeView.DROP_ON, tagName, null,
+                               PlacesUtils.getConcreteItemGuid(elt._placesNode));
           dropPoint.beforeIndex = eltIndex;
           dropPoint.folderElt = elt;
         } else {
           // Drop after this folder.
           let beforeIndex =
             (eltIndex == this._rootElt.childNodes.length - 1) ?
             -1 : eltIndex + 1;
 
           dropPoint.ip =
             new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
-                               beforeIndex, Ci.nsITreeView.DROP_BEFORE);
+                               beforeIndex, Ci.nsITreeView.DROP_BEFORE, null, null,
+                               PlacesUtils.getConcreteItemGuid(this._resultNode));
           dropPoint.beforeIndex = beforeIndex;
         }
       } else {
         // This is a non-folder node or a read-only folder.
         // Drop before it with regards to RTL mode.
         let threshold = eltRect.width * 0.5;
         if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
           // Drop before this bookmark.
           dropPoint.ip =
             new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
-                               eltIndex, Ci.nsITreeView.DROP_BEFORE);
+                               eltIndex, Ci.nsITreeView.DROP_BEFORE, null, null,
+                               PlacesUtils.getConcreteItemGuid(this._resultNode));
           dropPoint.beforeIndex = eltIndex;
         } else {
           // Drop after this bookmark.
           let beforeIndex =
             eltIndex == this._rootElt.childNodes.length - 1 ?
             -1 : eltIndex + 1;
           dropPoint.ip =
             new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
-                               beforeIndex, Ci.nsITreeView.DROP_BEFORE);
+                               beforeIndex, Ci.nsITreeView.DROP_BEFORE, null, null,
+                               PlacesUtils.getConcreteItemGuid(this._resultNode));
           dropPoint.beforeIndex = beforeIndex;
         }
       }
     } else {
       // We are most likely dragging on the empty area of the
       // toolbar, we should drop after the last node.
       dropPoint.ip =
         new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
-                           -1, Ci.nsITreeView.DROP_BEFORE);
+                           -1, Ci.nsITreeView.DROP_BEFORE, null, null,
+                           PlacesUtils.getConcreteItemGuid(this._resultNode));
       dropPoint.beforeIndex = -1;
     }
 
     return dropPoint;
   },
 
   _setTimer: function PT_setTimer(aTime) {
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -32,43 +32,65 @@ const RELOAD_ACTION_MOVE = 3;
  *          The index within the container where we should insert
  * @param   aOrientation
  *          The orientation of the insertion. NOTE: the adjustments to the
  *          insertion point to accommodate the orientation should be done by
  *          the person who constructs the IP, not the user. The orientation
  *          is provided for informational purposes only!
  * @param   [optional] aTag
  *          The tag name if this IP is set to a tag, null otherwise.
- * @param   [optional] aDropNearItemId
- *          When defined we will calculate index based on this itemId
+ * @param   [optional] aDropNearNode
+ *          When defined we will calculate index based on this node
+ * @param   [optional] aGuid
+ *          The guid of the parent container
  * @constructor
  */
 function InsertionPoint(aItemId, aIndex, aOrientation, aTagName = null,
-                        aDropNearItemId = false) {
+                        aDropNearNode = null, aGuid = null) {
+
   this.itemId = aItemId;
+  this.guid = aGuid;
   this._index = aIndex;
   this.orientation = aOrientation;
   this.tagName = aTagName;
-  this.dropNearItemId = aDropNearItemId;
+  this.dropNearNode = aDropNearNode;
 }
 
 InsertionPoint.prototype = {
   set index(val) {
     return this._index = val;
   },
 
+ // TODO (Bug 1382991): Remove this backwards compatibility shim.
   promiseGuid() {
-    return PlacesUtils.promiseItemGuid(this.itemId);
+    return this.guid || PlacesUtils.promiseItemGuid(this.itemId);
   },
 
+  // TODO (Bug 1382991): Remove this backwards compatibility shim.
   get index() {
-    if (this.dropNearItemId > 0) {
+    if (this.dropNearNode && typeof this.dropNearNode != "number")
+      throw new Error("dropNearNode is not a number, use getIndex() instead?");
+    if (this.dropNearNode > 0) {
       // If dropNearItemId is set up we must calculate the real index of
       // the item near which we will drop.
-      var index = PlacesUtils.bookmarks.getItemIndex(this.dropNearItemId);
+      var index = PlacesUtils.bookmarks.getItemIndex(this.dropNearNode);
+      return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1;
+    }
+    return this._index;
+  },
+
+  async getIndex() {
+    // TODO (Bug 1382991): Remove this backwards compatibility check.
+    if (typeof this.dropNearNode == "number")
+      return this.index;
+
+    if (this.dropNearNode) {
+      // If dropNearNode is set up we must calculate the index of the item near
+      // which we will drop.
+      let index = (await PlacesUtils.bookmarks.fetch(this.dropNearNode.bookmarkGuid)).index;
       return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1;
     }
     return this._index;
   },
 
   get isTag() {
     return typeof(this.tagName) == "string";
   }
@@ -263,20 +285,20 @@ PlacesController.prototype = {
       break;
     case "placesCmd_open:privatewindow":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true);
       break;
     case "placesCmd_open:tab":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
       break;
     case "placesCmd_new:folder":
-      this.newItem("folder");
+      this.newItem("folder").catch(Components.utils.reportError);
       break;
     case "placesCmd_new:bookmark":
-      this.newItem("bookmark");
+      this.newItem("bookmark").catch(Components.utils.reportError);
       break;
     case "placesCmd_new:separator":
       this.newSeparator().catch(Components.utils.reportError);
       break;
     case "placesCmd_show:info":
       this.showBookmarkPropertiesForSelection();
       break;
     case "placesCmd_moveBookmarks":
@@ -723,55 +745,56 @@ PlacesController.prototype = {
   },
 
   /**
    * Shows the Add Bookmark UI for the current insertion point.
    *
    * @param aType
    *        the type of the new item (bookmark/livemark/folder)
    */
-  newItem: function PC_newItem(aType) {
+  async newItem(aType) {
     let ip = this._view.insertionPoint;
     if (!ip)
       throw Cr.NS_ERROR_NOT_AVAILABLE;
 
     let performed =
       PlacesUIUtils.showBookmarkDialog({ action: "add",
                                          type: aType,
                                          defaultInsertionPoint: ip,
                                          hiddenRows: [ "folderPicker" ]
                                        }, window.top);
     if (performed) {
       // Select the new item.
       let insertedNodeId = PlacesUtils.bookmarks
-                                      .getIdForItemAt(ip.itemId, ip.index);
+                                      .getIdForItemAt(ip.itemId, await ip.getIndex());
       this._view.selectItems([insertedNodeId], false);
     }
   },
 
   /**
    * Create a new Bookmark separator somewhere.
    */
   async newSeparator() {
     var ip = this._view.insertionPoint;
     if (!ip)
       throw Cr.NS_ERROR_NOT_AVAILABLE;
 
+    let index = await ip.getIndex();
     if (!PlacesUIUtils.useAsyncTransactions) {
-      let txn = new PlacesCreateSeparatorTransaction(ip.itemId, ip.index);
+      let txn = new PlacesCreateSeparatorTransaction(ip.itemId, index);
       PlacesUtils.transactionManager.doTransaction(txn);
       // Select the new item.
       let insertedNodeId = PlacesUtils.bookmarks
-                                      .getIdForItemAt(ip.itemId, ip.index);
+                                      .getIdForItemAt(ip.itemId, index);
       this._view.selectItems([insertedNodeId], false);
       return;
     }
 
     let txn = PlacesTransactions.NewSeparator({ parentGuid: await ip.promiseGuid(),
-                                                index: ip.index });
+                                                index });
     let guid = await txn.transact();
     let itemId = await PlacesUtils.promiseItemId(guid);
     // Select the new item.
     this._view.selectItems([itemId], false);
   },
 
   /**
    * Opens a dialog for moving the selected nodes.
@@ -837,35 +860,37 @@ PlacesController.prototype = {
    * A range is an array of adjacent nodes in a view.
    * @param   [in] range
    *          An array of nodes to remove. Should all be adjacent.
    * @param   [out] transactions
    *          An array of transactions.
    * @param   [optional] removedFolders
    *          An array of folder nodes that have already been removed.
    */
-  _removeRange: function PC__removeRange(range, transactions, removedFolders) {
+  async _removeRange(range, transactions, removedFolders) {
     NS_ASSERT(transactions instanceof Array, "Must pass a transactions array");
     if (!removedFolders)
       removedFolders = [];
 
     for (var i = 0; i < range.length; ++i) {
       var node = range[i];
       if (this._shouldSkipNode(node, removedFolders))
         continue;
 
       if (PlacesUtils.nodeIsTagQuery(node.parent)) {
         // This is a uri node inside a tag container.  It needs a special
         // untag transaction.
         var tagItemId = PlacesUtils.getConcreteItemId(node.parent);
         var uri = NetUtil.newURI(node.uri);
         if (PlacesUIUtils.useAsyncTransactions) {
           let tag = node.parent.title;
-          if (!tag)
-            tag = PlacesUtils.bookmarks.getItemTitle(tagItemId);
+          if (!tag) {
+            let tagGuid = PlacesUtils.getConcreteItemGuid(node.parent);
+            tag = (await PlacesUtils.bookmarks.fetch(tagGuid)).title;
+          }
           transactions.push(PlacesTransactions.Untag({ uri, tag }));
         } else {
           let txn = new PlacesUntagURITransaction(uri, [tagItemId]);
           transactions.push(txn);
         }
       } else if (PlacesUtils.nodeIsTagQuery(node) && node.parent &&
                PlacesUtils.nodeIsQuery(node.parent) &&
                PlacesUtils.asQuery(node.parent).queryOptions.resultType ==
@@ -923,18 +948,19 @@ PlacesController.prototype = {
    * @param   txnName
    *          See |remove|.
    */
   async _removeRowsFromBookmarks(txnName) {
     var ranges = this._view.removableSelectionRanges;
     var transactions = [];
     var removedFolders = [];
 
-    for (var i = 0; i < ranges.length; i++)
-      this._removeRange(ranges[i], transactions, removedFolders);
+    for (let range of ranges) {
+      await this._removeRange(range, transactions, removedFolders);
+    }
 
     if (transactions.length > 0) {
       if (PlacesUIUtils.useAsyncTransactions) {
         await PlacesTransactions.batch(transactions);
       } else {
         var txn = new PlacesAggregatedTransaction(txnName, transactions);
         PlacesUtils.transactionManager.doTransaction(txn);
       }
@@ -1267,17 +1293,17 @@ PlacesController.prototype = {
 
     let itemsToSelect = [];
     if (PlacesUIUtils.useAsyncTransactions) {
       if (ip.isTag) {
         let urls = items.filter(item => "uri" in item).map(item => Services.io.newURI(item.uri));
         await PlacesTransactions.Tag({ urls, tag: ip.tagName }).transact();
       } else {
         await PlacesTransactions.batch(async function() {
-          let insertionIndex = ip.index;
+          let insertionIndex = await ip.getIndex();
           let parent = await ip.promiseGuid();
 
           for (let item of items) {
             let doCopy = action == "copy";
 
             // If this is not a copy, check for safety that we can move the
             // source, otherwise report an error and fallback to a copy.
             if (!doCopy &&
@@ -1294,51 +1320,51 @@ PlacesController.prototype = {
             // position.  If index is DEFAULT_INDEX, items are just appended.
             if (insertionIndex != PlacesUtils.bookmarks.DEFAULT_INDEX)
               insertionIndex++;
           }
         });
       }
     } else {
       let transactions = [];
-      let insertionIndex = ip.index;
-      for (let i = 0; i < items.length; ++i) {
+      let insertionIndex = await ip.getIndex();
+      for (let index = insertionIndex, i = 0; i < items.length; ++i) {
         if (ip.isTag) {
           // Pasting into a tag container means tagging the item, regardless of
           // the requested action.
           let tagTxn = new PlacesTagURITransaction(NetUtil.newURI(items[i].uri),
                                                    [ip.itemId]);
           transactions.push(tagTxn);
           continue;
         }
 
         // Adjust index to make sure items are pasted in the correct position.
         // If index is DEFAULT_INDEX, items are just appended.
-        if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX)
-          insertionIndex = ip.index + i;
+        if (index != PlacesUtils.bookmarks.DEFAULT_INDEX)
+          index += i;
 
         // If this is not a copy, check for safety that we can move the source,
         // otherwise report an error and fallback to a copy.
         if (action != "copy" && !PlacesControllerDragHelper.canMoveUnwrappedNode(items[i])) {
           Components.utils.reportError("Tried to move an unmovable Places " +
                                        "node, reverting to a copy operation.");
           action = "copy";
         }
         transactions.push(
           PlacesUIUtils.makeTransaction(items[i], type, ip.itemId,
-                                        insertionIndex, action == "copy")
+                                        index, action == "copy")
         );
       }
 
       let aggregatedTxn = new PlacesAggregatedTransaction("Paste", transactions);
       PlacesUtils.transactionManager.doTransaction(aggregatedTxn);
 
       for (let i = 0; i < transactions.length; ++i) {
         itemsToSelect.push(
-          PlacesUtils.bookmarks.getIdForItemAt(ip.itemId, ip.index + i)
+          PlacesUtils.bookmarks.getIdForItemAt(ip.itemId, insertionIndex + i)
         );
       }
     }
 
     // Cut/past operations are not repeatable, so clear the clipboard.
     if (action == "cut") {
       this._clearClipboard();
     }
@@ -1588,23 +1614,23 @@ var PlacesControllerDragHelper = {
         let spec = uri ? uri.spec : "about:blank";
         nodes = [{ uri: spec,
                    title: data.label,
                    type: PlacesUtils.TYPE_X_MOZ_URL}];
       } else
         throw new Error("bogus data was passed as a tab");
 
       for (let unwrapped of nodes) {
-        let index = insertionPoint.index;
+        let index = await insertionPoint.getIndex();
 
         // Adjust insertion index to prevent reversal of dragged items. When you
         // drag multiple elts upward: need to increment index or each successive
         // elt will be inserted at the same index, each above the previous.
         let dragginUp = insertionPoint.itemId == unwrapped.parent &&
-                        index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
+                        index < (await PlacesUtils.bookmarks.fetch(unwrapped.itemGuid)).index;
         if (index != -1 && dragginUp)
           index += movedCount++;
 
         // If dragging over a tag container we should tag the item.
         if (insertionPoint.isTag) {
           let uri = NetUtil.newURI(unwrapped.uri);
           let tagItemId = insertionPoint.itemId;
           if (PlacesUIUtils.useAsyncTransactions)
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -1006,27 +1006,28 @@ var gEditItemOverlay = {
 
   async newFolder() {
     let ip = this._folderTree.insertionPoint;
 
     // default to the bookmarks menu folder
     if (!ip || ip.itemId == PlacesUIUtils.allBookmarksFolderId) {
       ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
                               PlacesUtils.bookmarks.DEFAULT_INDEX,
-                              Ci.nsITreeView.DROP_ON);
+                              Ci.nsITreeView.DROP_ON, null, null,
+                              PlacesUtils.bookmarks.menuGuid);
     }
 
     // XXXmano: add a separate "New Folder" string at some point...
     let title = this._element("newFolderButton").label;
     if (PlacesUIUtils.useAsyncTransactions) {
       let parentGuid = await ip.promiseGuid();
-      await PlacesTransactions.NewFolder({ parentGuid, title, index: ip.index })
+      await PlacesTransactions.NewFolder({ parentGuid, title, index: await ip.getIndex() })
                               .transact().catch(Components.utils.reportError);
     } else {
-      let txn = new PlacesCreateFolderTransaction(title, ip.itemId, ip.index);
+      let txn = new PlacesCreateFolderTransaction(title, ip.itemId, await ip.getIndex());
       PlacesUtils.transactionManager.doTransaction(txn);
     }
 
     this._folderTree.focus();
     this._folderTree.selectItems([ip.itemId]);
     PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
     this._folderTree.selectItems([this._lastNewItem]);
     this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -88,17 +88,19 @@
             let eltY = elt.boxObject.y - scrollboxOffset;
             let eltHeight = elt.boxObject.height;
 
             if (!elt._placesNode) {
               // If we are dragging over a non places node drop at the end.
               dropPoint.ip = new InsertionPoint(
                                   PlacesUtils.getConcreteItemId(resultNode),
                                   -1,
-                                  Ci.nsITreeView.DROP_ON);
+                                  Ci.nsITreeView.DROP_ON,
+                                  null,
+                                  PlacesUtils.getConcreteItemGuid(resultNode));
               // We can set folderElt if we are dropping over a static menu that
               // has an internal placespopup.
               let isMenu = elt.localName == "menu" ||
                  (elt.localName == "toolbarbutton" &&
                   elt.getAttribute("type") == "menu");
               if (isMenu && elt.lastChild &&
                   elt.lastChild.hasAttribute("placespopup"))
                 dropPoint.folderElt = elt;
@@ -113,47 +115,52 @@
               // This is a folder or a tag container.
               if (eventY - eltY < eltHeight * 0.20) {
                 // If mouse is in the top part of the element, drop above folder.
                 dropPoint.ip = new InsertionPoint(
                                     PlacesUtils.getConcreteItemId(resultNode),
                                     -1,
                                     Ci.nsITreeView.DROP_BEFORE,
                                     tagName,
-                                    elt._placesNode.itemId);
+                                    elt._placesNode,
+                                    PlacesUtils.getConcreteItemGuid(resultNode));
                 return dropPoint;
               } else if (eventY - eltY < eltHeight * 0.80) {
                 // If mouse is in the middle of the element, drop inside folder.
                 dropPoint.ip = new InsertionPoint(
                                     PlacesUtils.getConcreteItemId(elt._placesNode),
                                     -1,
                                     Ci.nsITreeView.DROP_ON,
-                                    tagName);
+                                    tagName,
+                                    null,
+                                    PlacesUtils.getConcreteItemGuid(elt._placesNode));
                 dropPoint.folderElt = elt;
                 return dropPoint;
               }
             } else if (eventY - eltY <= eltHeight / 2) {
               // This is a non-folder node or a readonly folder.
               // If the mouse is above the middle, drop above this item.
               dropPoint.ip = new InsertionPoint(
                                   PlacesUtils.getConcreteItemId(resultNode),
                                   -1,
                                   Ci.nsITreeView.DROP_BEFORE,
                                   tagName,
-                                  elt._placesNode.itemId);
+                                  elt._placesNode,
+                                  PlacesUtils.getConcreteItemGuid(resultNode));
               return dropPoint;
             }
 
             // Drop below the item.
             dropPoint.ip = new InsertionPoint(
                                 PlacesUtils.getConcreteItemId(resultNode),
                                 -1,
                                 Ci.nsITreeView.DROP_AFTER,
                                 tagName,
-                                elt._placesNode.itemId);
+                                elt._placesNode,
+                                PlacesUtils.getConcreteItemGuid(resultNode));
             return dropPoint;
         ]]></body>
       </method>
 
       <!-- Sub-menus should be opened when the mouse drags over them, and closed
            when the mouse drags off.  The overFolder object manages opening and
            closing of folders when the mouse hovers. -->
       <field name="_overFolder"><![CDATA[({
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -466,17 +466,17 @@
 
       <method name="_getInsertionPoint">
         <parameter name="index"/>
         <parameter name="orientation"/>
         <body><![CDATA[
           var result = this.result;
           var resultview = this.view;
           var container = result.root;
-          var dropNearItemId = -1;
+          var dropNearNode = null;
           NS_ASSERT(container, "null container");
           // When there's no selection, assume the container is the container
           // the view is populated from (i.e. the result's itemId).
           if (index != -1) {
             var lastSelected = resultview.nodeForTreeIndex(index);
             if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
               // If the last selected item is an open container, append _into_
               // it, rather than insert adjacent to it.
@@ -510,17 +510,17 @@
                 index = -1;
               } else if (queryOptions.excludeItems ||
                          queryOptions.excludeQueries ||
                          queryOptions.excludeReadOnlyFolders) {
                 // Some item may be invisible, insert near last selected one.
                 // We don't replace index here to avoid requests to the db,
                 // instead it will be calculated later by the controller.
                 index = -1;
-                dropNearItemId = lastSelected.itemId;
+                dropNearNode = lastSelected;
               } else {
                 var lsi = container.getChildIndex(lastSelected);
                 index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
               }
             }
           }
 
           if (PlacesControllerDragHelper.disallowInsertion(container))
@@ -532,17 +532,18 @@
             tagName = container.title;
             if (!tagName)
               return null;
           }
 
           return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                                     index, orientation,
                                     tagName,
-                                    dropNearItemId);
+                                    dropNearNode,
+                                    PlacesUtils.getConcreteItemGuid(container));
         ]]></body>
       </method>
 
       <!-- nsIPlacesView -->
       <method name="selectAll">
         <body><![CDATA[
           this.view.selection.selectAll();
         ]]></body>
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -1368,17 +1368,17 @@ PlacesTreeView.prototype = {
       return false;
 
     let ip = this._getInsertionPoint(aRow, aOrientation);
     return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer);
   },
 
   _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
     let container = this._result.root;
-    let dropNearItemId = -1;
+    let dropNearNode = null;
     // When there's no selection, assume the container is the container
     // the view is populated from (i.e. the result's itemId).
     if (index != -1) {
       let lastSelected = this.nodeForTreeIndex(index);
       if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
         // If the last selected item is an open container, append _into_
         // it, rather than insert adjacent to it.
         container = lastSelected;
@@ -1416,17 +1416,17 @@ PlacesTreeView.prototype = {
           index = -1;
         } else if (queryOptions.excludeItems ||
                  queryOptions.excludeQueries ||
                  queryOptions.excludeReadOnlyFolders) {
           // Some item may be invisible, insert near last selected one.
           // We don't replace index here to avoid requests to the db,
           // instead it will be calculated later by the controller.
           index = -1;
-          dropNearItemId = lastSelected.itemId;
+          dropNearNode = lastSelected;
         } else {
           let lsi = container.getChildIndex(lastSelected);
           index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
         }
       }
     }
 
     if (PlacesControllerDragHelper.disallowInsertion(container))
@@ -1438,17 +1438,18 @@ PlacesTreeView.prototype = {
       tagName = container.title;
       if (!tagName)
         return null;
     }
 
     return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                               index, orientation,
                               tagName,
-                              dropNearItemId);
+                              dropNearNode,
+                              PlacesUtils.getConcreteItemGuid(container));
   },
 
   drop: function PTV_drop(aRow, aOrientation, aDataTransfer) {
     // We are responsible for translating the |index| and |orientation|
     // parameters into a container id and index within the container,
     // since this information is specific to the tree view.
     let ip = this._getInsertionPoint(aRow, aOrientation);
     if (ip) {