Bug 1094818 - Use Bookmarks.jsm in controller.js. draft
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 18 Jul 2017 19:18:02 +0200
changeset 613018 334266f963fddf0eb7b2d568e85fcd5983923d32
parent 612966 0faada5c2f308f101ab7c54a87b3dce80b97d0e3
child 613020 20c708d001111ee9c38b667110bccc2746d9df88
push id69691
push usermak77@bonardo.net
push dateFri, 21 Jul 2017 11:29:10 +0000
bugs1094818
milestone56.0a1
Bug 1094818 - Use Bookmarks.jsm in controller.js. 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
@@ -561,17 +561,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
@@ -580,19 +580,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",
@@ -660,17 +664,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;
     }
 
@@ -1076,33 +1081,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) {
@@ -579,17 +581,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);
@@ -626,18 +628,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
@@ -218,17 +218,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);
   },
 
@@ -1415,68 +1416,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) {