Backout bug 498130 due to persistent startup crash on Windows Mobile (bug 519854)
authorDietrich Ayala <dietrich@mozilla.com>
Wed, 30 Sep 2009 17:21:39 -0700
changeset 33314 e12943b9ce6a16fd1fe5ff21b099cab8de3f5579
parent 33313 c5ff0838c35f25c14e271c5e0b68c208569174c5
child 33315 58c5864cb9c6614dbcb3a652f2112f8a189cb3f0
push idunknown
push userunknown
push dateunknown
bugs498130, 519854
milestone1.9.3a1pre
Backout bug 498130 due to persistent startup crash on Windows Mobile (bug 519854)
browser/base/content/browser-places.js
browser/components/places/content/controller.js
browser/components/places/content/editBookmarkOverlay.js
browser/components/places/content/menu.xml
browser/components/places/content/toolbar.xml
browser/components/places/content/tree.xml
browser/components/places/content/treeView.js
browser/components/places/content/utils.js
browser/components/places/src/nsPlacesTransactionsService.js
browser/components/places/tests/browser/browser_views_liveupdate.js
browser/components/places/tests/unit/test_placesTxn.js
browser/fuel/src/fuelApplication.js
toolkit/components/places/public/nsINavBookmarksService.idl
toolkit/components/places/public/nsINavHistoryService.idl
toolkit/components/places/src/nsLivemarkService.js
toolkit/components/places/src/nsNavBookmarks.cpp
toolkit/components/places/src/nsNavBookmarks.h
toolkit/components/places/src/nsNavHistoryQuery.cpp
toolkit/components/places/src/nsNavHistoryResult.cpp
toolkit/components/places/src/nsNavHistoryResult.h
toolkit/components/places/src/nsPlacesDBFlush.js
toolkit/components/places/src/nsTaggingService.js
toolkit/components/places/tests/bookmarks/test_393498.js
toolkit/components/places/tests/bookmarks/test_bookmarks.js
toolkit/components/places/tests/bookmarks/test_onBeforeItemRemoved_observer.js
toolkit/components/places/tests/bookmarks/test_removeItem.js
toolkit/components/places/tests/chrome/test_329534.xul
toolkit/components/places/tests/chrome/test_341972a.xul
toolkit/components/places/tests/chrome/test_342484.xul
toolkit/components/places/tests/chrome/test_371798.xul
toolkit/components/places/tests/chrome/test_381357.xul
toolkit/components/places/tests/queries/test_abstime-annotation-domain.js
toolkit/components/places/tests/queries/test_abstime-annotation-uri.js
toolkit/components/places/tests/queries/test_querySerialization.js
toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
toolkit/components/places/tests/unit/nsDummyObserver.js
toolkit/components/places/tests/unit/test_457698_crash.js
toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1191,31 +1191,30 @@ var PlacesStarButton = {
     this._batching = true;
   },
 
   onEndUpdateBatch: function PSB_onEndUpdateBatch() {
     this.updateState();
     this._batching = false;
   },
   
-  onItemAdded: function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType) {
+  onItemAdded: function PSB_onItemAdded(aItemId, aFolder, aIndex) {
     if (!this._batching && !this._starred)
       this.updateState();
   },
 
-  onBeforeItemRemoved: function() {},
+  onBeforeItemRemoved: function PSB_onBeforeItemRemoved(aItemId) {
+  },
 
-  onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex,
-                                            aItemType) {
+  onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex) {
     if (!this._batching)
       this.updateState();
   },
 
   onItemChanged: function PSB_onItemChanged(aItemId, aProperty,
-                                            aIsAnnotationProperty, aNewValue,
-                                            aLastModified, aItemType) {
+                                            aIsAnnotationProperty, aValue) {
     if (!this._batching && aProperty == "uri")
       this.updateState();
   },
 
   onItemVisited: function() { },
   onItemMoved: function() { }
 };
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -146,34 +146,37 @@ PlacesController.prototype = {
     case "cmd_copy":
     case "placesCmd_copy":
       return this._view.hasSelection;
     case "cmd_paste":
     case "placesCmd_paste":
       return this._canInsert(true) && this._isClipboardDataPasteable();
     case "cmd_selectAll":
       if (this._view.selType != "single") {
-        var rootNode = this._view.getResultNode();
-        if (rootNode.containerOpen && rootNode.childCount > 0)
+        var result = this._view.getResult();
+        if (result) {
+          var container = asContainer(result.root);
+          if (container.containerOpen && container.childCount > 0)
             return true;
+        }
       }
       return false;
     case "placesCmd_open":
     case "placesCmd_open:window":
     case "placesCmd_open:tab":
       var selectedNode = this._view.selectedNode;
       return selectedNode && PlacesUtils.nodeIsURI(selectedNode);
     case "placesCmd_new:folder":
     case "placesCmd_new:livemark":
       return this._canInsert();
     case "placesCmd_new:bookmark":
       return this._canInsert();
     case "placesCmd_new:separator":
       return this._canInsert() &&
-             !asQuery(this._view.getResultNode()).queryOptions.excludeItems &&
+             !asQuery(this._view.getResult().root).queryOptions.excludeItems &&
              this._view.getResult().sortingMode ==
                  Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
     case "placesCmd_show:info":
       var selectedNode = this._view.selectedNode;
       if (selectedNode &&
           PlacesUtils.getConcreteItemId(selectedNode) != -1  &&
           !PlacesUtils.nodeIsLivemarkItem(selectedNode))
         return true;
@@ -441,17 +444,17 @@ PlacesController.prototype = {
    *          node are set on its corresponding object as properties.
    * Notes:
    *   1) This can be slow, so don't call it anywhere performance critical!
    *   2) A single-object array corresponding the root node is returned if
    *      there's no selection.
    */
   _buildSelectionMetadata: function PC__buildSelectionMetadata() {
     var metadata = [];
-    var root = this._view.getResultNode();
+    var root = this._view.getResult().root;
     var nodes = this._view.getSelectionNodes();
     if (nodes.length == 0)
       nodes.push(root); // See the second note above
 
     for (var i=0; i < nodes.length; i++) {
       var nodeData = {};
       var node = nodes[i];
       var nodeType = node.type;
@@ -1003,16 +1006,17 @@ PlacesController.prototype = {
    * Removes the set of selected ranges from history.
    */
   _removeRowsFromHistory: function PC__removeRowsFromHistory() {
     // Other containers are history queries, just delete from history
     // history deletes are not undoable.
     var nodes = this._view.getSelectionNodes();
     var URIs = [];
     var bhist = PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory);
+    var resultView = this._view.getResultView();
     var root = this._view.getResultNode();
 
     for (var i = 0; i < nodes.length; ++i) {
       var node = nodes[i];
       if (PlacesUtils.nodeIsURI(node)) {
         var uri = PlacesUtils._uri(node.uri);
         // avoid trying to delete the same url twice
         if (URIs.indexOf(uri) < 0) {
@@ -1078,17 +1082,17 @@ PlacesController.prototype = {
    *          as part of another operation.
    */
   remove: function PC_remove(aTxnName) {
     if (!this._hasRemovableSelection(false))
       return;
 
     NS_ASSERT(aTxnName !== undefined, "Must supply Transaction Name");
 
-    var root = this._view.getResultNode();
+    var root = this._view.getResult().root;
 
     if (PlacesUtils.nodeIsFolder(root)) 
       this._removeRowsFromBookmarks(aTxnName);
     else if (PlacesUtils.nodeIsQuery(root)) {
       var queryType = asQuery(root).queryOptions.queryType;
       if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS)
         this._removeRowsFromBookmarks(aTxnName);
       else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -1060,18 +1060,17 @@ var gEditItemOverlay = {
     case "unload":
       this.uninitPanel(false);
       break;
     }
   },
 
   // nsINavBookmarkObserver
   onItemChanged: function EIO_onItemChanged(aItemId, aProperty,
-                                            aIsAnnotationProperty, aValue,
-                                            aLastModified, aItemType) {
+                                            aIsAnnotationProperty, aValue) {
     if (this._itemId != aItemId) {
       if (aProperty == "title") {
         // If the title of a folder which is listed within the folders
         // menulist has been changed, we need to update the label of its
         // representing element.
         var menupopup = this._folderMenuList.menupopup;
         for (var i=0; i < menupopup.childNodes.length; i++) {
           if (menupopup.childNodes[i].folderId == aItemId) {
@@ -1141,29 +1140,29 @@ var gEditItemOverlay = {
       if (siteURI)
         siteURISpec = siteURI.spec;
       this._initTextField("siteLocationField", siteURISpec);
       break;
     }
   },
 
   onItemMoved: function EIO_onItemMoved(aItemId, aOldParent, aOldIndex,
-                                        aNewParent, aNewIndex, aItemType) {
+                                        aNewParent, aNewIndex) {
     if (aItemId != this._itemId ||
         aNewParent == this._getFolderIdFromMenuList())
       return;
 
     var folderItem = this._getFolderMenuItem(aNewParent);
 
     // just setting selectItem _does not_ trigger oncommand, so we don't
     // recurse
     this._folderMenuList.selectedItem = folderItem;
   },
 
-  onItemAdded: function EIO_onItemAdded(aItemId, aFolder, aIndex, aItemType) {
+  onItemAdded: function EIO_onItemAdded(aItemId, aFolder, aIndex) {
     this._lastNewItem = aItemId;
   },
 
   onBeginUpdateBatch: function() { },
   onEndUpdateBatch: function() { },
   onBeforeItemRemoved: function() { },
   onItemRemoved: function() { },
   onItemVisited: function() { },
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -73,16 +73,18 @@
 
       <!-- markers for start and end of valid places items -->
       <field name="_startMarker">-1</field>
       <field name="_endMarker">-1</field>
 
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
+      <field name="_built">false</field>
+
       <method name="onDragOver">
         <parameter name="aEvent"/>
         <parameter name="aFlavour"/>
         <parameter name="aDragSession"/>
         <body><![CDATA[
           PlacesControllerDragHelper.currentDropTarget = aEvent.target;
           // check if we have a valid dropPoint
           var dropPoint = this._getDropPoint(aEvent);
@@ -529,19 +531,19 @@
     </handlers>
   </binding>
 
 
   <binding id="places-menupopup"
            extends="chrome://browser/content/places/menu.xml#places-popup-base">
     <implementation>
       <destructor><![CDATA[
+        this._resultNode = null;
         if (this._result) {
-          this._resultNode.containerOpen = false;
-          this._resultNode = null;
+          this._result.root.containerOpen = false;
           this._result.viewer = null;
           this._result = null;
         }
       ]]></destructor>
 
       <field name="_initialized">false</field>
       <method name="_ensureInitialized">
         <body><![CDATA[
@@ -567,23 +569,22 @@
 
       <method name="onPopupShowing">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var popup = aEvent.target;
           var resultNode = popup._resultNode;
           if (!resultNode.containerOpen)
             resultNode.containerOpen = true;
-          if (!popup.parentNode._built)
+          if (!popup._built)
             this._rebuild(popup);
         ]]></body>
       </method>
 
       <field name="_result">null</field>
-      <field name="_resultNode">null</field>
 
       <!-- nsIPlacesView -->
       <method name="getResult">
         <body><![CDATA[
           return this._result;
         ]]></body>
       </method>
 
@@ -593,35 +594,44 @@
           this._ensureInitialized();
           return this._resultNode;
         ]]></body>
       </method>
 
       <method name="removeItem">
         <parameter name="child"/>
         <body><![CDATA[
+          if (PlacesUtils.nodeIsContainer(child.node)) {
+            for (var i=0; i < this._containerNodesMap.length; i++) {
+              if (this._containerNodesMap[i].resultNode == child.node) {
+                this._containerNodesMap.splice(i, 1);
+                break;
+              }
+            }
+          }
+
           // if document.popupNode pointed to this child, null it out,
           // otherwise controller's command-updating may rely on the removed
           // item still being "selected".
           if (document.popupNode == child)
             document.popupNode = null;
-
           child.parentNode.removeChild(child);
 
           if (this._endMarker != -1)
             this._endMarker--;
         ]]></body>
       </method>
 
       <method name="insertNewItem">
         <parameter name="aChild"/>
         <parameter name="aParentPopup"/>
         <parameter name="aBefore"/>
         <body><![CDATA[
-          var element = PlacesUIUtils.createMenuItemForNode(aChild);
+          var element =
+            PlacesUIUtils.createMenuItemForNode(aChild, this._containerNodesMap);
 
           if (aBefore)
             aParentPopup.insertBefore(element, aBefore);
           else {
             // Add the new element to the menu.  If there is static content at
             // the end of the menu, add the element before that.  Otherwise,
             // just add to the end.
             if (aParentPopup._endMarker != -1) {
@@ -674,220 +684,220 @@
             }
           }
           else {
             // This menu is empty.  If there is no static content, add
             // an element to show it is empty.
             if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
               this._showEmptyMenuItem(aPopup);
           }
-          aPopup.parentNode._built = true;
+          aPopup._built = true;
         ]]></body>
       </method>
 
       <!-- nsINavHistoryResultViewer -->
       <field name="_viewer"><![CDATA[({
         _self: this,
 
+        _getPopupForContainer:
+        function PMV__getPopupForContainer(aNode) {
+          if (this._self._resultNode == aNode)
+            return this._self;
+
+          for (var i=0; i < this._self._containerNodesMap.length; i++) {
+            if (this._self._containerNodesMap[i].resultNode == aNode)
+              return this._self._containerNodesMap[i].domNode;
+          }
+          throw("Container view not found");
+        },
+
         get result() {
           return this._self._result;
         },
 
         set result(val) {
           // some methods (e.g. getURLsFromContainer) temporarily null out the
           // viewer when they do temporary changes to the view, this does _not_
           // call setResult(null), but then, we're called again with the result
           // object which is already set for this viewer. At that point,
           // we should do nothing.
           if (this._self._result != val) {
             if (this._self._result)
-              this._self._resultNode.containerOpen = false;
-
-            this._self.parentNode._built = false;
+              this._self._result.root.containerOpen = false;
+            this._built = false;
+            this._self._containerNodesMap = [];
+            this._self._resultNode = val.root;
             this._self._result = val;
-            if (val) {
-              this._self._resultNode = val.root;
-              this._self._resultNode._DOMElement = this._self.parentNode;
-            }
-            else
-              this._self._resultNode = null;
           }
           return val;
         },
 
-        nodeInserted: function PMV_nodeInserted(aParentNode, aNode, aIndex) {
-          let parentElt = aParentNode._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-
-          if (!parentElt._built)
+        itemInserted: function PMV_itemInserted(aParentNode, aNode, aIndex) {
+          var popup = this._getPopupForContainer(aParentNode);
+          if (!popup._built)
             return;
 
-          // parentElt is the <menu> element for the container,
-          // we need the <menupopup>
-          let popup = parentElt.firstChild;
-
-          let index = popup._startMarker + 1 + aIndex;
-          let before = popup.childNodes[index] || null;
+          var index = popup._startMarker + 1 + aIndex;
+          var before = popup.childNodes[index] || null;
           this._self.insertNewItem(aNode, popup, before);
           if (popup._emptyMenuItem)
             popup._emptyMenuItem.hidden = true;
         },
 
-        nodeRemoved: function PMV_nodeRemoved(aParentNode, aNode, aIndex) {
-          let parentElt = aParentNode._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-
-          if (!parentElt._built)
+        itemRemoved: function PMV_itemRemoved(aParentNode, aNode, aIndex) {
+          var popup = this._getPopupForContainer(aParentNode);
+          if (!popup._built)
             return;
 
-          // parentElt is the <menu> element for the container,
-          // we need the <menupopup>
-          let popup = parentElt.firstChild;
-
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-          popup.removeChild(nodeElt);
-
-          // Figure out if we need to show the "<Empty>" menu-item.
-          // TODO Bug 517701: This doesn't seem to handle the case of an empty
-          // root (parentElt == this._self.parentNode).
-          if (!popup.hasChildNodes() ||
-              (popup.childNodes.length == 1 &&
-              popup.firstChild == popup._emptyMenuItem))
-            this._self._showEmptyMenuItem(popup);
-
-          if (popup._endMarker != -1)
-            popup._endMarker--;
-        },
-
-        nodeMoved:
-        function PMV_nodeMoved(aNode,
-                               aOldParent, aOldIndex,
-                               aNewParent, aNewIndex) {
-          // Note: the current implementation of moveItem does not actually
-          // use this notification when the item in question is moved from one
-          // folder to another.  Instead, it calls nodeRemoved and nodeInserted
-          // for the two folders.  Thus, we can assume aOldParent == aNewParent.
-
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // If our root node is a folder, it might be moved. There's nothing
-          // we need to do in that case.
-          if (nodeElt == this._self.parentNode)
-            return;
-
-          // Move the node.
-          let popup = nodeElt.parentNode;
-          popup.removeChild(nodeElt);
-          popup.insertBefore(nodeElt, popup.childNodes[aNewIndex]);
+          var children = popup.childNodes;
+          for (var i = popup._startMarker + 1; i < children.length; i++) {
+            if (children[i].node == aNode) {
+              this._self.removeItem(children[i]);
+              if (!popup.hasChildNodes() ||
+                  (popup.childNodes.length == 1 &&
+                   popup.firstChild == popup._emptyMenuItem)) {
+                this._self._showEmptyMenuItem(popup);
+              }
+              return;
+            }
+          }
         },
 
-        nodeTitleChanged: function PMV__nodeTitleChanged(aNode, aNewTitle) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // There's no UI representation for the root node, thus there's
-          // nothing to be done when the title changes.
-          if (nodeElt == this._self.parentNode)
+        itemMoved:
+        function PMV_itemMoved(aItem, aOldParent, aOldIndex, aNewParent,
+                               aNewIndex) {
+          // This cannot actually happen yet (see IDL)
+          if (aNewParent != aOldParent)
             return;
 
-          nodeElt.label = aNewTitle || PlacesUIUtils.getBestTitle(aNode);
-        },
-
-        nodeURIChanged: function PMV_nodeURIChanged(aNode, aURIString) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          nodeElt.setAttribute("scheme",
-                               PlacesUIUtils.guessUrlSchemeForUI(aURIString));
-        },
-
-        nodeIconChanged: function PMV_nodeIconChanged(aNode) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // There's no UI representation for the root node, thus there's
-          // nothing to be done when the icon changes.
-          if (nodeElt == this._self.parentNode)
-            return;
-
-          var icon = aNode.icon;
-          if (icon) {
-            if (nodeElt.getAttribute("image") != icon)
-              nodeElt.setAttribute("image", icon);
-          }
-          else
-            nodeElt.removeAttribute("image");
-        },
-
-        nodeAnnotationChanged:
-        function PMV_nodeAnnotationChanged(aNode, aAnno) {
-          // Ensure the changed annotation is a livemark one.
-          if (/^livemark\//.test(aAnno) &&
-              PlacesUtils.nodeIsLivemarkContainer(aNode)) {
-            let nodeElt = aNode._DOMElement;
-            NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-            if (!nodeElt.hasAttribute("livemark"))
-              nodeElt.setAttribute("livemark", "true");
-
-            // Add or remove the livemark status menuitem.
-            PlacesUIUtils.ensureLivemarkStatusMenuItem(nodeElt.firstChild);
+          var popup = this._getPopupForContainer(aNewParent);
+          var index = popup._startMarker + 1 + aNewIndex;
+          var children = popup.childNodes;
+          for (var i = popup._startMarker + 1; i < children.length; i++) {
+            var menuItem = children[i];
+            if (menuItem.node == aItem) {
+              popup.removeChild(menuItem);
+              popup.insertBefore(menuItem, children[index]);
+              return;
+            }
           }
         },
 
-        nodeHistoryDetailsChanged: function() { },
-        nodeTagsChanged: function() { },
-        nodeDateAddedChanged: function() { },
-        nodeLastModifiedChanged: function() { },
-        nodeKeywordChanged: function() { },
+        itemChanged: function PMV_itemChanged(aNode) {
+          // this check can be removed once we fix bug #382397
+          var parentNode = aNode.parent;
+          if (!parentNode)
+            return;
 
-        nodeReplaced:
-        function PMV_nodeReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
-          let parentElt = aParentNode._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
+          if (PlacesUtils.nodeIsSeparator(aNode)) {
+            // nothing to do when a separator changes
+            return;
+          }
 
-          if (!parentElt._built)
+          var popup = this._getPopupForContainer(parentNode);
+          if (!popup._built)
             return;
 
-          // parentElt is the <menu> element for the container,
-          // we need the <menupopup>.
-          let popup = parentElt.firstChild;
+          var children = popup.childNodes;
+          var menuitem;
+          for (var i = popup._startMarker + 1; i < children.length; i++) {
+            if (children[i].node == aNode) {
+              menuitem = children[i];
+              break;
+            }
+          }
+
+          var iconURI = aNode.icon;
+          if (iconURI) {
+            var spec = iconURI.spec;
+            if (menuitem.getAttribute("image") != spec)
+              menuitem.setAttribute("image", spec);
+          }
+          else
+            menuitem.removeAttribute("image");
+
+          var title = PlacesUIUtils.getBestTitle(aNode);
+          if (menuitem.getAttribute("label") != title)
+            menuitem.setAttribute("label", title);
 
-          let nodeElt = aOldNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
+          if (PlacesUtils.nodeIsLivemarkContainer(aNode)) {
+            if (!menuitem.hasAttribute("livemark"))
+              menuitem.setAttribute("livemark", "true");
+            // If this is a livemark container check if the status menuitem has
+            // to be added or removed.
+            PlacesUIUtils.ensureLivemarkStatusMenuItem(menuitem.firstChild);
+          }
+          else if (PlacesUtils.nodeIsURI(aNode)) {
+              menuitem.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aNode.uri));
+          }
+        },
 
-          // No worries: If nodeElt is the last item (i.e. no nextSibling),
-          // insertNewItem will insert the new element as the last item.
-          let next = nodeElt.nextSibling;
-          this._self.removeItem(nodeElt);
-          this._self.insertNewItem(aNewNode, popup, next);
+        itemReplaced:
+        function PMV_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
+          var popup = this._getPopupForContainer(aParentNode);
+          if (!popup._built)
+            return;
+
+          var children = popup.childNodes;
+          for (var i = popup._startMarker + 1; i < children.length; i++) {
+            if (children[i].node == aOldNode) {
+              var next = children[i].nextSibling;
+              this._self.removeItem(children[i]);
+              this._self.insertNewItem(aNewNode, popup, next);
+              return;
+            }
+          }
         },
 
         containerOpened: function PMV_containerOpened(aNode) {
           this.invalidateContainer(aNode);
         },
 
         containerClosed: function PMV_containerClosed(aNode) {
           this.invalidateContainer(aNode);
         },
-
+ 
         invalidateContainer: function PMV_invalidateContainer(aContainer) {
-          // Do nothing if the entire view is already marked un-built.
-          if (!this._self.parentNode._built)
+          if (!this._self._built)
             return;
 
-          let containerNodeElt = aContainer._DOMElement;
-          NS_ASSERT(containerNodeElt, "node must have _DOMElement set");
-          containerNodeElt._built = false;
+          function isChildOf(node, container) {
+            var parent = node.parent;
+            while (parent) {
+              if (parent == container)
+                return true;
+              parent = parent.parent;
+            }
+            return false;
+          }
+
+          var popupToRebuild = null;
+          for (var i=0; i < this._self._containerNodesMap.length; i++) {
+            var node = this._self._containerNodesMap[i].resultNode;
 
-          // If the menupopup is open we should live-update it.
-          if (containerNodeElt.open)
-            this._self._rebuild(containerNodeElt.firstChild);
+            if (node == aContainer)
+              popupToRebuild = this._self._containerNodesMap[i].domNode;
+            if (isChildOf(node, aContainer)) {
+              this._self._containerNodesMap.splice(i,1);
+              i--;
+            }
+          }
+
+          if (!popupToRebuild)
+            popupToRebuild = this._self;
+          popupToRebuild._built = false;
+
+          // if the menupopup is open we should live-update it
+          if (popupToRebuild.parentNode.open)
+            this._self._rebuild(popupToRebuild);
+        },
+
+        invalidateAll: function PMV_invalidateAll() {
+          this._self._containerNodesMap.splice(0);
+          this._self._built = false;
         },
 
         sortingChanged: function PMV_sortingChanged(aSortingMode) {
         }
       })]]></field>
 
       <!-- nsIPlacesView -->
       <property name="place">
@@ -899,17 +909,16 @@
           var queries = { }, options = { };
           PlacesUtils.history.queryStringToQueries(val, queries, { }, options);
           if (!queries.value.length)
             queries.value = [PlacesUtils.history.getNewQuery()];
           var result =
             PlacesUtils.history.executeQueries(queries.value,
                                                queries.value.length,
                                                options.value);
-
           result.viewer = this._viewer;
           return val;
         ]]></setter>
       </property>
 
       <!-- nsIPlacesView -->
       <property name="hasSelection">
         <getter><![CDATA[
--- a/browser/components/places/content/toolbar.xml
+++ b/browser/components/places/content/toolbar.xml
@@ -19,31 +19,30 @@
 # Portions created by the Initial Developer are Copyright (C) 2005-2006
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Annie Sullivan <annie.sullivan@gmail.com>
 #   Ben Goodger <beng@google.com>
 #   Myk Melez <myk@mozilla.org>
 #   Marco Bonardo <mak77@bonardo.net>
-#   Asaf Romano <mano@mozilla.com>
 #
 # 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 *****
+# ***** END LICENSE BLOCK ***** 
 
 
 <!DOCTYPE bindings [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
 %browserDTD;
 ]>
 
 <bindings id="placesToolbarBindings"
@@ -95,71 +94,38 @@
       ]]></constructor>
 
       <destructor><![CDATA[
         this._scrollbox.removeEventListener("overflow", this, false);
         this._scrollbox.removeEventListener("underflow", this, false);
         window.removeEventListener("resize", this, false);
 
         if (this._result) {
-          this._resultNode.containerOpen = false;
-          this._resultNode = null;
           this._result.viewer = null;
           this._result = null;
         }
       ]]></destructor>
 
       <property name="controller"
                 readonly="true"
                 onget="return this._controller;"/>
 
       <method name="_init">
         <body><![CDATA[
-          // XBL bug is in the middle...
-          // When toolbar customization is opened, this binding is attached
-          // again, as a result of adding the item under the wrapper.  However,
-          // the binding isn't detached from the "original" hbox element due
-          // to bug 83635.
-          //
-          // Then, when the customization dialog is closed, the binding is
-          // attached the third time, as a result of adding our element back to
-          // the toolbar.
-          //
-          // So, We'll just continue using the original binding, which was
-          // never removed, and avoid using the new bindings.  This means that
-          // this workaround will work just until bug 83635 is fixed.
-          //
-          // However, when the binding is "reconstructed", we do need to add
-          // back the event listeners and the places controller.
-          //
-          // Note: we could avoid part of this mess by moving the "Bookmark
-          // Toolbar Items" placeholder out of this binding.
+        this._controller = new PlacesController(this);
+        this.controllers.appendController(this._controller);
 
-          // We also need to avoid initializing _result and _resultNode and
-          // _controller as XBL fields.  Otherwise, they'll be unset when the
-          // "extra" bindings are applied.
-
-          this._scrollbox.addEventListener("overflow", this, false);
-          this._scrollbox.addEventListener("underflow", this, false);
-          window.addEventListener("resize", this, false);
+        this._scrollbox.addEventListener("overflow", this, false);
+        this._scrollbox.addEventListener("underflow", this, false);
+        window.addEventListener("resize", this, false);
 
-          if (this._result === undefined) {
-            this._result = null;
-            this._resultNode = null;
-            if (this.hasAttribute("place")) {
-              // Do the initial build.
-              this.place = this.place;
-            }
-          }
-
-          // Attach the places controller.
-          if (!this._controller) 
-            this._controller = new PlacesController(this);
-
-          this.controllers.appendController(this._controller);
+        if (this.hasAttribute("place")) {
+          // Do the initial build.
+          this.place = this.place;
+        }
         ]]></body>
       </method>
 
       <field name="_scrollbox">
         document.getAnonymousElementByAttribute(this, "class",
                                                 "bookmarks-toolbar-items")
       </field>
       <field name="_dropIndicator">
@@ -171,117 +137,139 @@
       </field>
       <field name="_chevronPopup">
         document.getAnonymousElementByAttribute(this, "anonid", "chevronPopup")
       </field>
 
       <field name="_openedMenuButton">null</field>
       <field name="_allowPopupShowing">true</field>
 
+      <field name="_result">null</field>
+      <field name="_resultNode">null</field>
+
       <field name="_isRTL">
         document.defaultView.getComputedStyle(this.parentNode, "")
                             .direction == "rtl"
       </field>
 
       <!-- nsIPlacesView -->
       <method name="getResult">
         <body><![CDATA[
           return this._result;
         ]]></body>
       </method>
 
       <!-- nsIPlacesView -->
       <method name="getResultNode">
         <body><![CDATA[
-          return this._resultNode;
+          return this._result.root;
         ]]></body>
       </method>
 
       <method name="_rebuild">
         <body><![CDATA[
           // Clear out references to existing nodes, since they will be removed
           // and re-added.
           if (this._overFolder.node)
             this._clearOverFolder();
           this._openedMenuButton = null;
 
           while (this.hasChildNodes())
             this.removeChild(this.firstChild);
 
-          let cc = this._resultNode.childCount;
+          var rootNode = this._result.root;
+          var cc = rootNode.childCount;
           for (let i = 0; i < cc; ++i)
-            this.insertNewItem(this._resultNode.getChild(i), null);
+            this.insertNewItem(rootNode.getChild(i), null);
 
           if (this._chevronPopup.hasAttribute("type")) {
             // Chevron has already been initialized, but since we are forcing
             // a rebuild of the toolbar, it has to be rebuilt.
             // Otherwise, it will be initialized when the toolbar overflows.
             this._chevronPopup.place = this.place;
           }
+
+          while (chevronPopup.hasChildNodes())
+            this._chevronPopup.removeChild(this._chevronPopup.lastChild);
         ]]></body>
       </method>
 
       <method name="insertNewItem">
         <parameter name="aChild"/>
         <parameter name="aBefore"/>
         <body><![CDATA[
           var type = aChild.type;
           var button;
           if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
             button = document.createElement("toolbarseparator");
           else {
             button = document.createElement("toolbarbutton");
             button.className = "bookmark-item";
             button.setAttribute("label", aChild.title);
-            var icon = aChild.icon;
-            if (icon)
-              button.setAttribute("image", icon);
+            var iconURI = aChild.icon;
+            var iconURISpec = "";
+            if (iconURI) {
+              iconURISpec = iconURI.spec;
+              button.setAttribute("image", iconURISpec);
+            }
 
             if (PlacesUtils.containerTypes.indexOf(type) != -1) {
               button.setAttribute("type", "menu");
               button.setAttribute("container", "true");
 
               if (PlacesUtils.nodeIsQuery(aChild)) {
                 button.setAttribute("query", "true");
                 if (PlacesUtils.nodeIsTagQuery(aChild))
                   button.setAttribute("tagContainer", "true");
               }
               else if (PlacesUtils.nodeIsLivemarkContainer(aChild))
                 button.setAttribute("livemark", "true");
 
               var popup = document.createElement("menupopup");
               popup.setAttribute("placespopup", "true");
               button.appendChild(popup);
+              popup._result = this._result;
               popup._resultNode = asContainer(aChild);
 #ifndef XP_MACOSX
               popup.setAttribute("context", "placesContext");
 #endif
+              this._containerNodesMap.push({ resultNode: aChild,
+                                             domNode: popup });
             }
-            else if (PlacesUtils.nodeIsURI(aChild))
+            else if (PlacesUtils.nodeIsURI(aChild)) {
               button.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
+            }
           }
 
           button.node = aChild;
-          aChild._DOMElement = button;
+          button.node.viewIndex = 0;
           if (aBefore)
             this.insertBefore(button, aBefore);
           else
             this.appendChild(button);
         ]]></body>
       </method>
 
       <method name="removeItem">
         <parameter name="child"/>
         <body><![CDATA[
+          if (PlacesUtils.nodeIsContainer(child.node)) {
+            for (let i = 0; i < this._containerNodesMap.length; i++) {
+              if (this._containerNodesMap[i].resultNode == child.node) {
+                this._containerNodesMap.splice(i, 1);
+                break;
+              }
+            }
+          }
+
           // if document.popupNode pointed to this child, null it out,
           // otherwise controller's command-updating may rely on the removed
           // item still being "selected".
           if (document.popupNode == child)
             document.popupNode = null;
-
           child.parentNode.removeChild(child);
         ]]></body>
       </method>
 
       <method name="_updateChevronPopupNodesVisibility">
         <body><![CDATA[
           for (let i = 0; i < this._chevronPopup.childNodes.length; i++) {
             this._chevronPopup.childNodes[i].hidden =
@@ -385,41 +373,41 @@
         ]]></body>
       </method>
 
       <!-- nsIPlacesView -->
       <property name="place">
         <getter><![CDATA[
           return this.getAttribute("place");
         ]]></getter>
-        <setter><![CDATA[
+        <setter><![CDATA[ 
           this.setAttribute("place", val);
 
           var history = PlacesUtils.history;
           var queries = { }, options = { };
           history.queryStringToQueries(val, queries, { }, options);
-          if (!queries.value.length)
+          if (!queries.value.length) 
             queries.value = [history.getNewQuery()];
           try {
             var result =
               history.executeQueries(queries.value, queries.value.length,
                                      options.value);
             result.viewer = this._viewer;
           }
           catch(ex) {
             // Invalid query, or had no results.
-            // This is valid, eg: user deletes his bookmarks toolbar folder.
+            // This is valid, eg: user deletes their bookmarks toolbar folder. 
           }
           return val;
         ]]></setter>
       </property>
 
       <!-- nsIPlacesView -->
       <property name="hasSelection">
-        <getter><![CDATA[
+        <getter><![CDATA[ 
           return this.selectedNode != null;
         ]]></getter>
       </property>
 
       <!-- nsIPlacesView -->
       <method name="getSelectionNodes">
         <body><![CDATA[
           var selectedNode = this.selectedNode;
@@ -460,19 +448,19 @@
           }
           return null;
         ]]></getter>
       </property>
 
       <!-- nsIPlacesView -->
       <property name="insertionPoint">
         <getter><![CDATA[
-          // By default, the insertion point is at the top level, at the end.
+          // By default, the insertion point is at the top level, at the end. 
           var index = PlacesUtils.bookmarks.DEFAULT_INDEX;
-          var container = this._resultNode;
+          var container = this._result.root;
           var orientation = Ci.nsITreeView.DROP_BEFORE;
           var isTag = false;
 
           var selectedNode = this.selectedNode;
           if (selectedNode) {
             var popupNode = document.popupNode;
             if (!popupNode.node) {
               // If a static menuitem is selected the insertion point
@@ -493,272 +481,291 @@
 
           return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
                                     index, orientation, isTag);
         ]]></getter>
       </property>
 
       <!-- nsIPlacesView -->
       <method name="selectAll">
-        <body><![CDATA[
+        <body><![CDATA[ 
           // Nothing
         ]]></body>
       </method>
 
       <method name="selectItems">
         <body><![CDATA[
           // Nothing
         ]]></body>
       </method>
 
       <!-- nsINavHistoryResultViewer -->
       <field name="_viewer"><![CDATA[({
         _self: this,
 
+        _getPopupForContainer:
+        function PMV__getPopupForContainer(aNode) {
+          if (this._self._resultNode == aNode)
+            return this._self;
+
+          for (let i = 0; i < this._self._containerNodesMap.length; i++) {
+            if (this._self._containerNodesMap[i].resultNode == aNode)
+              return this._self._containerNodesMap[i].domNode;
+          }
+          throw("Container view not found");
+        },
+
         get result() {
           return this._self._result;
         },
 
         set result(val) {
           // some methods (e.g. getURLsFromContainer) temporarily null out the
           // viewer when they do temporary changes to the view, this does _not_
           // call setResult(null), but then, we're called again with the result
           // object which is already set for this viewer. At that point,
           // we should do nothing.
           if (this._self._result != val) {
             if (this._self._result)
-              this._self._resultNode.containerOpen = false;
-
+              this._self._result.root.containerOpen = false;
+            this._self._containerNodesMap = [];
             this._self._result = val;
-            if (val) {
-              this._self._resultNode = val.root;
-              this._self._resultNode._DOMElement = this._self;
-              // This calls _rebuild through invalidateContainer.
-              this._self._resultNode.containerOpen = true;
-            }
-            else
-              this._self._resultNode = null;
+            if (val) // this calls _rebuild through invalidateContainer
+              val.root.containerOpen = true;
           }
           return val;
         },
 
-        nodeInserted: function TV_V_nodeInserted(aParentNode, aNode, aIndex) {
-          let parentElt = aParentNode._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-
-          if (parentElt == this._self) {
-            // Node is on the toolbar.
-            let children = this._self.childNodes;
+        itemInserted: function TV_V_itemInserted(aParentNode, aNode, aIndex) {
+          // don't insert new items into the toolbar
+          // if the parent is not the root 
+          if (aParentNode == this._self.getResultNode()) {
+            var children = this._self.childNodes;
             this._self.insertNewItem(aNode,
               aIndex < children.length ? children[aIndex] : null);
             this._self.updateChevron();
           }
-          else if (parentElt._built) {
-            // Node is within a built menu.
-            let popup = parentElt.firstChild;
-            let before = popup.childNodes[aIndex] || null;
+          else {
+            var popup = this._getPopupForContainer(aParentNode);
+            if (!popup._built)
+              return;
+
+            var before = popup.childNodes[aIndex] || null;
             this._self.insertNewItemToPopup(aNode, popup, before);
             if (popup._emptyMenuItem)
               popup._emptyMenuItem.hidden = true;
           }
         },
 
-        nodeRemoved: function TV_V_nodeRemoved(aParentNode, aNode, aIndex) {
-          let parentElt = aParentNode._DOMElement;
-          let nodeElt = aNode._DOMElement;
-
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          if (parentElt == this._self) {
-            // Node is on the toolbar.
-            this._self.removeChild(nodeElt);
-            this._self.updateChevron();
+        itemRemoved: function TV_V_itemRemoved(aParentNode, aNode, aIndex) {
+          if (aParentNode == this._self.getResultNode()) {
+            var children = this._self.childNodes;
+            for (let i = 0; i < children.length; i++) {
+              if (children[i].node == aNode) {
+                this._self.removeItem(children[i]);
+                this._self.updateChevron();
+                return;
+              }
+            }
           }
-          else if (parentElt._built) {
-            // Node is within a built menu.
-            var popup = parentElt.firstChild;
-            popup.removeChild(nodeElt);
-            if (!popup.hasChildNodes() ||
-                (popup.childNodes.length == 1 &&
-                popup.firstChild == popup._emptyMenuItem))
-              this._self._showEmptyMenuItem(popup);
+          else {
+            var popup = this._getPopupForContainer(aParentNode);
+            if (!popup._built)
+              return;
 
-            if (popup._endMarker != -1)
-              popup._endMarker--;
+            var children = popup.childNodes;
+            for (let i = popup._startMarker + 1; i < children.length; i++) {
+              if (children[i].node == aNode) {
+                this._self.removeItem(children[i]);
+                if (!popup.hasChildNodes() ||
+                    (popup.childNodes.length == 1 &&
+                     popup.firstChild == popup._emptyMenuItem)) {
+                  this._self._showEmptyMenuItem(popup);
+                }
+                if (popup._endMarker != -1)
+                  popup._endMarker--;
+                return;
+              }
+            }
           }
         },
 
-        nodeMoved:
-        function TV_V_nodeMoved(aNode,
-                                aOldParent, aOldIndex,
-                                aNewParent, aNewIndex) {
-          // Note: the current implementation of moveItem does not actually
-          // use this notification when the item in question is moved from one
-          // folder to another.  Instead, it calls nodeRemoved and nodeInserted
-          // for the two folders.  Thus, we can assume aOldParent == aNewParent.
-
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // If our root node is a folder, it might be moved. There's nothing
-          // we need to do in that case.
-          if (nodeElt == this._self)
+        itemMoved:
+        function TV_V_itemMoved(aItem, aOldParent, aOldIndex, aNewParent,
+                                aNewIndex) {
+          // This cannot actually happen yet (see IDL)
+          if (aNewParent != aOldParent)
             return;
 
-          let parentElt = aNewParent._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-
-          if (parentElt == this._self) {
-            // Container is on the toolbar.
-
-            // Move the node.
-            this._self.removeChild(nodeElt);
-            this._self.insertBefore(nodeElt, this._self.childNodes[aNewIndex]);
-
-            // If the chevron popup is open, keep it in sync.
-            if (this._self._chevron.open) {
-              let chevronPopup = this._self._chevronPopup;
-              let menuitem = chevronPopup.childNodes[aOldIndex];
-              chevronPopup.removeChild(menuitem);
-              chevronPopup.insertBefore(menuitem,
-                                        chevronPopup.childNodes[aNewIndex]);
+          if (aNewParent == this._self.getResultNode()) {
+            var children = this._self.childNodes;
+            for (let i = 0; i < children.length; i++) {
+              var button = children[i];
+              if (button.node == aItem) {
+                this._self.removeChild(button);
+                this._self.insertBefore(button, children[aNewIndex]);
+                // If the chevron popup is open, keep it in sync.
+                if (this._self._chevron.open) {
+                  var chevronPopup = this._self._chevronPopup;
+                  var menuitem = chevronPopup.childNodes[i];
+                  chevronPopup.removeChild(menuitem);
+                  chevronPopup.insertBefore(menuitem,
+                                            chevronPopup.childNodes[aNewIndex]);
+                }
+                this._self.updateChevron();
+                return;
+              }
             }
-            this._self.updateChevron();
-          }
-          else if (parentElt._built) {
-            // Container is within a built menu.
-
-            // parentElt is the <menu> element for the container,
-            // we need the <menupopup>.
-            var popup = parentElt.firstChild;
-
-            // Move the node.
-            popup.removeChild(nodeElt);
-            popup.insertBefore(nodeElt, popup.childNodes[aNewIndex]);
-          }
-        },
-
-        nodeTitleChanged: function TV_V_nodeTitleChanged(aNode, aNewTitle) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // There's no UI representation for the root node, thus there's
-          // nothing to be done when the title changes.
-          if (nodeElt == this._self)
-            return;
-
-          if (nodeElt.parentNode == this._self) {
-            // Node is on the toolbar
-            nodeElt.label = aNewTitle;
-            this._self.updateChevron();
           }
           else {
-            // Node is within a built menu.
-            nodeElt.label = aNewTitle || PlacesUIUtils.getBestTitle(aNode);
+            var popup = this._getPopupForContainer(aNewParent);
+            var children = popup.childNodes;
+            for (let i = popup._startMarker + 1; i < children.length; i++) {
+              var menuItem = children[i];
+              if (menuItem.node == aItem) {
+                popup.removeChild(menuItem);
+                popup.insertBefore(menuItem, children[aNewIndex]);
+                return;
+              }
+            }
           }
         },
 
-        nodeURIChanged: function TV_V_nodeURIChanged(aNode, aURIString) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          nodeElt.setAttribute("scheme",
-                               PlacesUIUtils.guessUrlSchemeForUI(aURIString));
-        },
-
-        nodeIconChanged: function TV_V_nodeIconChanged(aNode) {
-          let nodeElt = aNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // There's no UI representation for the root node, thus there's
-          // nothing to be done when the icon changes.
-          if (nodeElt == this._self)
+        itemChanged: function TV_V_itemChanged(aNode) {
+          // this check can be removed once we fix bug #382397
+          var parentNode = aNode.parent;
+          if (!parentNode)
             return;
 
-          let icon = aNode.icon;
-          if (icon) {
-            if (nodeElt.getAttribute("image") != icon)
-              nodeElt.setAttribute("image", icon);
+          if (PlacesUtils.nodeIsSeparator(aNode)) {
+            // nothing to do when a separator changes
+            return;
+          }
+
+          var element;
+          var onToolbar = false;
+          if (parentNode == this._self.getResultNode()) {
+            onToolbar = true;
+            var children = this._self.childNodes;
+            for (let i = 0; i < children.length; i++) {
+              if (children[i].node == aNode) {
+                element = children[i];
+                break;
+              }
+            }
+            // Don't replace title on toolbarbuttons
+            var title = aNode.title;
+          }
+          else {
+            var popup = this._getPopupForContainer(parentNode);
+            if (!popup._built)
+              return;
+
+            var children = popup.childNodes;
+            for (let i = popup._startMarker + 1; i < children.length; i++) {
+              if (children[i].node == aNode) {
+                element = children[i];
+                break;
+              }
+            }
+            var title = PlacesUIUtils.getBestTitle(aNode);
+          }
+
+          var iconURI = aNode.icon;
+          if (iconURI) {
+            var spec = iconURI.spec;
+            if (element.getAttribute("image") != spec)
+              element.setAttribute("image", spec);
           }
           else
-            nodeElt.removeAttribute("image");
-        },
+            element.removeAttribute("image");
+
+          if (element.getAttribute("label") != title) {
+            element.setAttribute("label", title);
+            if (onToolbar)
+              this._self.updateChevron();
+          }
 
-        nodeAnnotationChanged:
-        function TV_V_nodeAnnotationChanged(aNode, aAnno) {
-          // Ensure the changed annotation is a livemark one.
-          if (/^livemark\//.test(aAnno) &&
-              PlacesUtils.nodeIsLivemarkContainer(aNode)) {
-            let nodeElt = aNode._DOMElement;
-            NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-            if (!nodeElt.hasAttribute("livemark"))
-              nodeElt.setAttribute("livemark", "true");
-
-            // Add or remove the livemark status menuitem.
-            PlacesUIUtils.ensureLivemarkStatusMenuItem(nodeElt.firstChild);
+          if (PlacesUtils.nodeIsLivemarkContainer(aNode)) {
+            if (!element.hasAttribute("livemark"))
+              element.setAttribute("livemark", "true");
+            // If this is a livemark container check if the status menuitem has
+            // to be added or removed.
+            PlacesUIUtils.ensureLivemarkStatusMenuItem(element.firstChild);
+          }
+          else if (PlacesUtils.nodeIsURI(aNode)) {
+            element.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aNode.uri));
           }
         },
 
-        nodeHistoryDetailsChanged: function() { },
-        nodeTagsChanged: function() { },
-        nodeDateAddedChanged: function() { },
-        nodeLastModifiedChanged: function() { },
-        nodeKeywordChanged: function() { },
-
-        nodeReplaced:
-        function TV_V_nodeReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
-          let nodeElt = aOldNode._DOMElement;
-          NS_ASSERT(nodeElt, "node must have _DOMElement set");
-
-          // No worries: If nodeElt is the last item (i.e. no nextSibling),
-          // insertNewItem/insertNewItemToPopup will insert the new element as
-          // the last item.
-          let next = nodeElt.nextSibling;
-
-          let parentElt = aParentNode._DOMElement;
-          NS_ASSERT(parentElt, "parent node must have _DOMElement set");
-          if (parentElt == this._self) {
-            // Node is on the toolbar.
-            this._self.removeItem(nodeElt);
-            this._self.insertNewItem(aNewNode, next);
-            this._self.updateChevron();
-          }
-          else if (parentElt._built) {
-            // Node is within a built menu.
-            let popup = parentElt.firstChild;
-            popup.removeItem(nodeElt);
-            this._self.insertNewItemToPopup(aNewNode, popup, next);
+        itemReplaced:
+        function TV_V_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
+          if (aParentNode == this._self.getResultNode()) {
+            var children = this._self.childNodes;
+            for (let i = 0; i < children.length; i++) {
+              if (children[i].node == aOldNode) {
+                var next = children[i].nextSibling;
+                this._self.removeItem(children[i]);
+                this._self.insertNewItem(aNewNode, next);
+                this._self.updateChevron();
+                return;
+              }
+            }
           }
         },
 
-        containerOpened: function TV_V_containerOpened(aContainer) {
-          this.invalidateContainer(aContainer);
+        containerOpened: function TV_V_containerOpened(aNode) {
+          this.invalidateContainer(aNode);
         },
 
-        containerClosed: function TV_V_containerClosed(aContainer) {
-          this.invalidateContainer(aContainer);
+        containerClosed: function TV_V_containerClosed(aNode) {
+          this.invalidateContainer(aNode);
         },
 
         invalidateContainer: function TV_V_invalidateContainer(aContainer) {
-          let containerNodeElt = aContainer._DOMElement;
-          NS_ASSERT(containerNodeElt, "node must have _DOMElement set");
+          if (aContainer == this._self.getResultNode()) {
+            this._self._containerNodesMap.splice(0);
+            this._self._rebuild();
+            return;
+          }
 
-          if (containerNodeElt == this._self) {
-            // Container is the toolbar itself.
-            this._self._rebuild();
+          function isChildOf(node, container) {
+            var parent = node.parent;
+            while (parent) {
+              if (parent == container)
+                return true;
+              parent = parent.parent;
+            }
+            return false;
           }
-          else if (containerNodeElt._built) {
-            // Container is a built menu.
-            containerNodeElt._built = false;
-            // If the menupopup is open we should live-update it.
-            if (containerNodeElt.open)
-              this._self._rebuildPopup(containerNodeElt.firstChild);
+
+          var popupToRebuild = null;
+          for (let i = 0; i < this._self._containerNodesMap.length; i++) {
+            var node = this._self._containerNodesMap[i].resultNode;
+            
+            if (node == aContainer)
+              popupToRebuild = this._self._containerNodesMap[i].domNode;
+            if (isChildOf(node, aContainer)) {
+              this._self._containerNodesMap.splice(i,1);
+              i--;
+            }
           }
+
+          if (popupToRebuild) {
+            popupToRebuild._built = false;
+
+            // if the menupopup is open we should live-update it
+            if (popupToRebuild.parentNode.open)
+              this._self._rebuildPopup(popupToRebuild);
+          }
+        },
+
+        invalidateAll: function TV_V_invalidateAll() {
+          this._self._containerNodesMap.splice(0);
+          this._self._rebuild();
         },
 
         sortingChanged: function TV_V_sortingChanged(aSortingMode) {
         }
       })]]></field>
 
       <property name="selType" onget="return 'single';"/>
 
@@ -796,17 +803,18 @@
         ]]></body>
       </method>
 
       <method name="insertNewItemToPopup">
         <parameter name="aChild"/>
         <parameter name="aParentPopup"/>
         <parameter name="aBefore"/>
         <body><![CDATA[
-          var element = PlacesUIUtils.createMenuItemForNode(aChild);
+          var element =
+            PlacesUIUtils.createMenuItemForNode(aChild, this._containerNodesMap);
 
           if (aBefore)
             aParentPopup.insertBefore(element, aBefore);
           else {
             // Add the new element to the menu.  If there is static content at
             // the end of the menu, add the element before that.  Otherwise,
             // just add to the end.
             if (aParentPopup._endMarker != -1) {
@@ -820,17 +828,17 @@
           if (aParentPopup._endMarker != -1)
             aParentPopup._endMarker++;
         ]]></body>
       </method>
 
       <method name="_containerPopupShowing">
         <parameter name="aPopup"/>
         <body><![CDATA[
-          if (!aPopup.parentNode._built)
+          if (!aPopup._built)
             this._rebuildPopup(aPopup);
         ]]></body>
       </method>
 
       <method name="_rebuildPopup">
         <parameter name="aPopup"/>
         <body><![CDATA[
           PlacesUIUtils.cleanPlacesPopup(aPopup);
@@ -855,17 +863,17 @@
             }
           }
           else {
             // This menu is empty.  If there is no static content, add
             // an element to show it is empty.
             if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
               this._showEmptyMenuItem(aPopup);
           }
-          aPopup.parentNode._built = true;
+          aPopup._built = true;
         ]]></body>
       </method>
 
       <field name="_overFolder"><![CDATA[
        (
          // Menu buttons should be opened when the mouse drags over them, and
          // closed when the mouse drags off. This object manages opening and
          // closing of folders when the mouse hovers.
@@ -901,17 +909,17 @@
         <body><![CDATA[
           // This function returns information about where to drop when
           // dragging over the toolbar.
           // The returned object has 3 properties:
           // - ip: the insertion point for the bookmarks service.
           // - beforeIndex: child index to drop before, for the drop indicator.
           // - folderNode: the folder to drop into, if applicable.
           var result = this.getResult();
-          if (!PlacesUtils.nodeIsFolder(this._resultNode))
+          if (!PlacesUtils.nodeIsFolder(result.root))
             return null;
 
           var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
           var xulNode = aEvent.target;
           if (xulNode.node) {
             let nodeRect = xulNode.getBoundingClientRect();
             let nodeIndex = Array.indexOf(this.childNodes, xulNode);
             if (PlacesUtils.nodeIsFolder(xulNode.node) &&
@@ -919,17 +927,17 @@
               // This is a folder.
               // If we are in the middle of it, drop inside it.
               // Otherwise, drop before it, with regards to RTL mode.
               let threshold = nodeRect.width * 0.25;
               if (this._isRTL ? (aEvent.clientX > nodeRect.right - threshold)
                               : (aEvent.clientX < nodeRect.left + threshold)) {
                 // Drop before this folder.
                 dropPoint.ip =
-                  new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
+                  new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
                                      nodeIndex, Ci.nsITreeView.DROP_BEFORE);
                 dropPoint.beforeIndex = nodeIndex;
               }
               else if (this._isRTL ? (aEvent.clientX > nodeRect.left + threshold)
                                    : (aEvent.clientX < nodeRect.right - threshold)) {
                 // Drop inside this folder.
                 dropPoint.ip =
                   new InsertionPoint(PlacesUtils.getConcreteItemId(xulNode.node),
@@ -938,49 +946,49 @@
                 dropPoint.beforeIndex = nodeIndex;
                 dropPoint.folderNode = xulNode;
               }
               else {
                 // Drop after this folder.
                 let beforeIndex =
                   (nodeIndex == this.childNodes.length - 1) ? -1 : nodeIndex + 1;
                 dropPoint.ip =
-                  new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
+                  new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
                                      beforeIndex, Ci.nsITreeView.DROP_BEFORE);
                 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 = nodeRect.width * 0.5;
               if (this._isRTL ? (aEvent.clientX > nodeRect.left + threshold)
                               : (aEvent.clientX < nodeRect.left + threshold)) {
                 // Drop before this bookmark.
                 dropPoint.ip =
-	                new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
+	                new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
 	                                   nodeIndex, Ci.nsITreeView.DROP_BEFORE);
                 dropPoint.beforeIndex = nodeIndex;
               }
               else {
                 // Drop after this bookmark.
                 let beforeIndex =
                   nodeIndex == this.childNodes.length - 1 ? -1 : nodeIndex + 1;
                 dropPoint.ip =
-                  new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
+                  new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
                                      beforeIndex, Ci.nsITreeView.DROP_BEFORE);
                 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),
+              new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
                                  -1, Ci.nsITreeView.DROP_BEFORE);
             dropPoint.beforeIndex = -1;
           }
 
           return dropPoint;
         ]]></body>
       </method>
 
@@ -1203,17 +1211,17 @@
         if (this._overFolder.node)
             this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
         this._draggedNode = null;
       ]]></handler>
 
-      <handler event="popupshowing" phase="capturing"><![CDATA[
+      <handler event="popupshowing" phase="capturing"><![CDATA[      
         if (!this._allowPopupShowing) {
           this._allowPopupShowing = true;
           event.preventDefault();
           return;
         }
 
         var popup = event.originalTarget;
 
--- a/browser/components/places/content/tree.xml
+++ b/browser/components/places/content/tree.xml
@@ -131,27 +131,42 @@
                                                   options);
           var callback;
           if (this.flatList) {
             var onOpenFlatContainer = this.onOpenFlatContainer;
             if (onOpenFlatContainer)
               callback = new Function("aContainer", onOpenFlatContainer);
           }
 
-          var treeView = new PlacesTreeView(this.flatList, callback);
+          var treeView = new PlacesTreeView(this.showRoot, this.flatList, callback);
           result.viewer = treeView;
           this.view = treeView;
           if (!this._controller) {
             this._controller = new PlacesController(this);
             this.controllers.appendController(this._controller);
           }
           this._cachedInsertionPoint = undefined;
         ]]></body>
       </method>
 
+      <property name="showRoot">
+        <getter><![CDATA[
+          return this.getAttribute("showRoot") == "true";
+        ]]></getter>
+        <setter><![CDATA[
+          if (this.showRoot != val) {
+            this.setAttribute("showRoot", val);
+            // reload with the last place set
+            if (this.place)
+              this.place = this.place;
+          }
+          return val;
+        ]]></setter>
+      </property>
+
       <property name="flatList">
         <getter><![CDATA[
           return this.getAttribute("flatList") == "true";
         ]]></getter>
         <setter><![CDATA[
           if (this.flatList != val) {
             this.setAttribute("flatList", val);
             // reload with the last place set
@@ -239,17 +254,17 @@
       <!-- 
         Causes a particular node to be selected in the tree, resulting in all 
         containers above the node in the hierarchy to be opened, so that the
         node is visible. 
         -->
       <method name="selectNode">
         <parameter name="node"/>
         <body><![CDATA[ 
-          var view = this.view;
+          var view = this.getResultView();
 
           var parent = node.parent;
           if (parent && !parent.containerOpen) {
             // Build a list of all of the nodes that are the parent of this one 
             // in the result. 
             var parents = [];
             var root = this.getResultNode();
             while (parent && parent != root) {
@@ -288,16 +303,27 @@
 
       <!-- nsIPlacesView -->
       <method name="getResultNode">
         <body><![CDATA[
           return this.getResult().root;
         ]]></body>
       </method>
 
+      <method name="getResultView">
+        <body><![CDATA[
+          try {
+            return this.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
+          }
+          catch (e) {
+          }
+          return null;
+        ]]></body>
+      </method>
+
       <!-- nsIPlacesView -->
       <property name="place">
         <getter><![CDATA[
           return this.getAttribute("place");
         ]]></getter>
         <setter><![CDATA[
           this.setAttribute("place", val);
 
@@ -324,17 +350,17 @@
       </property>
       
       <!-- nsIPlacesView -->
       <method name="getSelectionNodes">
         <body><![CDATA[ 
           var selection = this.view.selection;
           var rc = selection.getRangeCount();
           var nodes = [];
-          var resultview = this.view;
+          var resultview = this.getResultView();
           for (var i = 0; i < rc; ++i) {
             var min = { }, max = { };
             selection.getRangeAt(i, min, max);
 
             for (var j = min.value; j <= max.value; ++j)
               nodes.push(resultview.nodeForTreeIndex(j));
           }
           return nodes;
@@ -365,17 +391,17 @@
           // ... returning http://www.bar.com/ as part of the selection is 
           // redundant because it is implied by removing "Some Folder". We 
           // filter out all such redundancies since some partial amount of
           // the folder's children may be selected.          
           //
           var selection = this.view.selection;
           var rc = selection.getRangeCount();
           var nodes = [];
-          var resultview = this.view;
+          var resultview = this.getResultView();
           // This list is kept independently of the range selected (i.e. OUTSIDE
           // the for loop) since the row index of a container is unique for the
           // entire view, and we could have some really wacky selection and we
           // don't want to blow up.
           var containers = { };
           for (var i = 0; i < rc; ++i) {
             var range = [];
             var min = { }, max = { };
@@ -406,17 +432,17 @@
           var view = this.view;
           if (view.selection.count != 1)
             return null;
 
           var selection = view.selection;
           var min = { }, max = { };
           selection.getRangeAt(0, min, max);
 
-          return this.view.nodeForTreeIndex(min.value);
+          return this.getResultView().nodeForTreeIndex(min.value);
         ]]></getter>
       </property>
 
       <!-- nsIPlacesView -->
       <property name="insertionPoint">
         <getter><![CDATA[
           // invalidated on selection and focus changes
           if (this._cachedInsertionPoint !== undefined)
@@ -447,17 +473,17 @@
           //
           // Warning: It may be tempting to use tree indexes in this code, but
           //          you must not, since the tree is nested and as your tree 
           //          index may change when folders before you are opened and
           //          closed. You must convert your tree index to a node, and
           //          then use getIndexOfNode to find your absolute index in
           //          the parent container instead. 
           //
-          var resultView = this.view;
+          var resultView = this.getResultView();
           var selection = resultView.selection;
           var rc = selection.getRangeCount();
           var min = { }, max = { };
           selection.getRangeAt(rc - 1, min, max);
           
           // If the sole selection is a container, and we are not in
           // a flatlist, insert into it.
           // Note that this only applies to _single_ selections,
@@ -478,17 +504,17 @@
         ]]></getter>
       </property>
 
       <method name="_getInsertionPoint">
         <parameter name="index"/>
         <parameter name="orientation"/>
         <body><![CDATA[ 
           var result = this.getResult();
-          var resultview = this.view;
+          var resultview = this.getResultView();
           var container = result.root;
           var dropNearItemId = -1;
           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) {
@@ -642,17 +668,17 @@
           var result = this.getResult();
           var oldViewer = result.viewer;
           result.viewer = null;
           findNodes(this.getResultNode());
           result.viewer = oldViewer;
 
           // For all the nodes we've found, highlight the corresponding
           // index in the tree.
-          var resultview = this.view;
+          var resultview = this.getResultView();
           var selection = this.view.selection;
           selection.selectEventsSuppressed = true;
           selection.clearSelection();
           // Open nodes containing found items
           for (var i = 0; i < nodesToOpen.length; i++) {
             nodesToOpen[i].containerOpen = true;
           }
           for (var i = 0; i < nodes.length; i++) {
@@ -698,17 +724,17 @@
         <body><![CDATA[
           // Cache the dataTransfer for the view
           PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
 
           var row = { }, col = { }, child = { };
           this.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY,
                                        row, col, child);
           var node = row.value != -1 ?
-                     this.view.nodeForTreeIndex(row.value) :
+                     this.getResultView().nodeForTreeIndex(row.value) :
                      this.getResultNode();
           // cache the dropTarget for the view
           PlacesControllerDragHelper.currentDropTarget = node;
 
           // We have to calculate the orientation since view.canDrop will use
           // it and we want to be consistent with the dropfeedback
           var tbo = this.treeBoxObject;
           var rowHeight = tbo.rowHeight;
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -72,45 +72,119 @@ PlacesTreeView.prototype = {
         aIID.equals(Ci.nsINavHistoryResultTreeViewer) ||
         aIID.equals(Ci.nsISupports))
       return this;
 
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   /**
-   * This is called once both the result and the tree are set.
+   * This is called when the result or tree may have changed.
+   * It reinitializes everything. Result and/or tree can be null
+   * when calling.
    */
   _finishInit: function PTV__finishInit() {
+    if (this._tree && this._result)
+      this.sortingChanged(this._result.sortingMode);
+
+    var qoInt = Ci.nsINavHistoryQueryOptions;
+    var options = asQuery(this._result.root).queryOptions;
+
+    // if there is no tree, BuildVisibleList will clear everything for us
+    this._buildVisibleList();
+  },
+
+  _computeShowSessions: function PTV__computeShowSessions() {
+    NS_ASSERT(this._result, "Must have a result to show sessions!");
+    this._showSessions = false;
+
+    var options = asQuery(this._result.root).queryOptions;
+    NS_ASSERT(options, "navHistoryResults must have valid options");
+
+    if (!options.showSessions)
+      return; // sessions are off
+
+    var resultType = options.resultType;
+    if (resultType != Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT &&
+        resultType != Ci.nsINavHistoryQueryOptions.RESULTS_AS_FULL_VISIT)
+      return; // not visits
+
+    var sortType = this._result.sortingMode;
+    if (sortType != nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING &&
+        sortType != nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING)
+      return; // not date sorting
+
+    this._showSessions = true;
+  },
+
+  SESSION_STATUS_NONE: 0,
+  SESSION_STATUS_START: 1,
+  SESSION_STATUS_CONTINUE: 2,
+  _getRowSessionStatus: function PTV__getRowSessionStatus(aRow) {
+    var node = this._visibleElements[aRow].node;
+    if (!PlacesUtils.nodeIsVisit(node) || asVisit(node).sessionId == 0)
+      return this.SESSION_STATUS_NONE;
+
+    if (aRow == 0)
+      return this.SESSION_STATUS_START;
+
+    var previousNode = this._visibleElements[aRow - 1].node;
+    if (!PlacesUtils.nodeIsVisit(previousNode) ||
+        node.sessionId != asVisit(previousNode).sessionId)
+      return this.SESSION_STATUS_START;
+
+    return this.SESSION_STATUS_CONTINUE;
+  },
+
+  /**
+   * Call to completely rebuild the list of visible items. Note if there is no
+   * tree or root this will just clear out the list, so you can also call this
+   * when a tree is detached to clear the list.
+   */
+  _buildVisibleList: function PTV__buildVisibleList() {
     var selection = this.selection;
     if (selection)
       selection.selectEventsSuppressed = true;
 
-    this._rootNode._viewIndex = -1;
-    if (!this._rootNode.containerOpen) {
-      // This triggers containerOpened which then builds the visible section.
-      this._rootNode.containerOpen = true;
+    if (this._result) {
+      // Any current visible elements need to be marked as invisible.
+      for (var i = 0; i < this._visibleElements.length; i++) {
+        this._visibleElements[i].node.viewIndex = -1;
+      }
     }
-    else
-      this.invalidateContainer(this._rootNode);
+
+    var rootNode = this._result.root;
+    if (rootNode && this._tree) {
+      this._computeShowSessions();
 
-    // "Activate" the sorting column and update commands.
-    this.sortingChanged(this._result.sortingMode);
-
+      asContainer(rootNode);
+      if (this._showRoot) {
+        // List the root node
+        this._visibleElements.push(
+          { node: this._result.root, properties: null });
+        this._tree.rowCountChanged(0, 1);
+        this._result.root.viewIndex = 0;
+      }
+      else if (!rootNode.containerOpen) {
+        // this triggers containerOpened which then builds the visible
+        // section
+        rootNode.containerOpen = true;
+      }
+      else
+        this.invalidateContainer(rootNode);
+    }
     if (selection)
       selection.selectEventsSuppressed = false;
   },
 
-  _rootNode: null,
-
   /**
    * This takes a container and recursively appends visible elements to the
    * given array. This is used to build the visible element list (with
    * this._visibleElements passed as the array), or portions thereof (with
-   * a separate array that is merged with the main list later).
+   * a separate array that is merged with the main list later.
    *
    * aVisibleStartIndex is the visible index of the beginning of the 'aVisible'
    * array. When aVisible is this._visibleElements, this is 0. This is non-zero
    * when we are building up a sub-region for insertion. Then, this is the
    * index where the new array will be inserted into this._visibleElements.
    * It is used to compute each node's viewIndex.
    */
   _buildVisibleSection:
@@ -118,148 +192,147 @@ PlacesTreeView.prototype = {
   {
     if (!aContainer.containerOpen)
       return;  // nothing to do
 
     const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
     const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
 
     var cc = aContainer.childCount;
-    var sortingMode = this._result.sortingMode;
     for (var i=0; i < cc; i++) {
       var curChild = aContainer.getChild(i);
       var curChildType = curChild.type;
 
-      // Don't display separators when sorted.
+      // don't display separators when sorted
       if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
-        if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
-          curChild._viewIndex = -1;
+        if (this._result.sortingMode !=
+            Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
+          curChild.viewIndex = -1;
           continue;
         }
       }
 
-      // Add the node to the visible-nodes array and set its viewIndex.
-      curChild._viewIndex = aVisibleStartIndex + aVisible.length;
-      aVisible.push(curChild);
+      // add item
+      curChild.viewIndex = aVisibleStartIndex + aVisible.length;
+      aVisible.push({ node: curChild, properties: null });
 
-      // Recursively do containers.
-      if (!this._flatList &&
-          curChild instanceof Ci.nsINavHistoryContainerResultNode) {
+      // recursively do containers
+      if (!this._flatList && PlacesUtils.containerTypes.indexOf(curChildType) != -1) {
+        asContainer(curChild);
+
         var resource = this._getResourceForNode(curChild);
         var isopen = resource != null &&
                      PlacesUIUtils.localStore.HasAssertion(resource, openLiteral,
                                                            trueLiteral, true);
         if (isopen != curChild.containerOpen)
           aToOpen.push(curChild);
         else if (curChild.containerOpen && curChild.childCount > 0)
           this._buildVisibleSection(curChild, aVisible, aToOpen, aVisibleStartIndex);
       }
     }
   },
 
   /**
-   * This counts how many rows a node takes in the tree.  For containers it
-   * will count the node itself plus any child node following it.
+   * This counts how many rows an item takes in the tree, that is, the
+   * item itself plus any nodes following it with an increased indent.
+   * This allows you to figure out how many rows an item (=1) or a
+   * container with all of its children takes.
    */
-  _countVisibleRowsForNode: function PTV__countVisibleRowsForNode(aNode) {
-    if (aNode == this._rootNode)
+  _countVisibleRowsForItem: function PTV__countVisibleRowsForItem(aNode) {
+    if (aNode == this._result.root)
       return this._visibleElements.length;
 
-    var viewIndex = aNode._viewIndex;
-    NS_ASSERT(viewIndex >= 0, "Node is not visible, no rows to count");
+    var viewIndex = aNode.viewIndex;
+    NS_ASSERT(viewIndex >= 0, "Item is not visible, no rows to count");
     var outerLevel = aNode.indentLevel;
     for (var i = viewIndex + 1; i < this._visibleElements.length; i++) {
-      if (this._visibleElements[i].indentLevel <= outerLevel)
+      if (this._visibleElements[i].node.indentLevel <= outerLevel)
         return i - viewIndex;
     }
     // this node plus its children occupy the bottom of the list
     return this._visibleElements.length - viewIndex;
   },
 
   /**
    * This is called by containers when they change and we need to update
    * everything about the container. We build a new visible section with
    * the container as a separate object so we first know how the list
-   * changes.
+   * changes. This way we only have to do one realloc/memcpy to update
+   * the list.
    *
    * We also try to be smart here about redrawing the screen.
    */
   _refreshVisibleSection: function PTV__refreshVisibleSection(aContainer) {
     NS_ASSERT(this._result, "Need to have a result to update");
     if (!this._tree)
       return;
 
-    // The root node is invisible.
-    if (aContainer != this._rootNode) {
-      if (aContainer._viewIndex < 0 ||
-          aContainer._viewIndex > this._visibleElements.length)
+    // The root node is invisible if showRoot is not set. Otherwise aContainer
+    // must be visible
+    if (this._showRoot || aContainer != this._result.root) {
+      if (aContainer.viewIndex < 0 ||
+          aContainer.viewIndex > this._visibleElements.length)
         throw "Trying to expand a node that is not visible";
 
-      NS_ASSERT(this._visibleElements[aContainer._viewIndex] == aContainer,
+      NS_ASSERT(this._visibleElements[aContainer.viewIndex].node == aContainer,
                 "Visible index is out of sync!");
     }
 
-    var startReplacement = aContainer._viewIndex + 1;
-    var replaceCount = this._countVisibleRowsForNode(aContainer);
+    var startReplacement = aContainer.viewIndex + 1;
+    var replaceCount = this._countVisibleRowsForItem(aContainer);
 
-    // We don't replace the container node itself so we should decrease the
-    // replaceCount by 1, unless the container is our root node, which isn't
-    // visible.
-    if (aContainer != this._rootNode)
-      replaceCount -= 1;
+    // We don't replace the container item itself so we decrease the
+    // replaceCount by 1. We don't do so though if there is no visible item
+    // for the container. This happens when aContainer is the root node and
+    // showRoot is not set.
+    if (aContainer.viewIndex != -1)
+      replaceCount-=1;
 
-    // Persist selection state.
+    // Persist selection state
     var previouslySelectedNodes = [];
     var selection = this.selection;
     var rc = selection.getRangeCount();
     for (var rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
       var min = { }, max = { };
       selection.getRangeAt(rangeIndex, min, max);
       var lastIndex = Math.min(max.value, startReplacement + replaceCount -1);
-      // If this range does not overlap the replaced chunk, we don't need to
+      // if this range does not overlap the replaced chunk we don't need to
       // persist the selection.
       if (max.value < startReplacement || min.value > lastIndex)
         continue;
-
-      // If this range starts before the replaced chunk, we should persist from
-      // startReplacement to lastIndex.
+      // if this range starts before the replaced chunk we should persist from
+      // startReplacement to lastIndex
       var firstIndex = Math.max(min.value, startReplacement);
-      for (var nodeIndex = firstIndex; nodeIndex <= lastIndex; nodeIndex++) {
-        // Mark the node invisible if we're about to remove it,
-        // otherwise we'll try to select it later.
-        var node = this._visibleElements[nodeIndex];
-        if (nodeIndex >= startReplacement &&
-            nodeIndex < startReplacement + replaceCount)
-          node._viewIndex = -1;
+      for (var nodeIndex = firstIndex; nodeIndex <= lastIndex; nodeIndex++)
+        previouslySelectedNodes.push(
+          { node: this._visibleElements[nodeIndex].node, oldIndex: nodeIndex });
+    }
 
-        previouslySelectedNodes.push(
-          { node: node, oldIndex: nodeIndex });
-      }
-    }
+    // Mark the removes as invisible
+    for (var i = 0; i < replaceCount; i++)
+      this._visibleElements[startReplacement + i].node.viewIndex = -1;
 
     // Building the new list will set the new elements' visible indices.
     var newElements = [];
     var toOpenElements = [];
     this._buildVisibleSection(aContainer,
                               newElements, toOpenElements, startReplacement);
 
-    // Actually update the visible list.
-    // XXX: We can probably make this more efficient using splice through
-    // Function.apply.
+    // actually update the visible list
     this._visibleElements =
       this._visibleElements.slice(0, startReplacement).concat(newElements)
           .concat(this._visibleElements.slice(startReplacement + replaceCount,
                                               this._visibleElements.length));
 
     // If the new area has a different size, we'll have to renumber the
     // elements following the area.
     if (replaceCount != newElements.length) {
       for (var i = startReplacement + newElements.length;
-           i < this._visibleElements.length; i++) {
-        this._visibleElements[i]._viewIndex = i;
+           i < this._visibleElements.length; i ++) {
+        this._visibleElements[i].node.viewIndex = i;
       }
     }
 
     // now update the number of elements
     selection.selectEventsSuppressed = true;
     this._tree.beginUpdateBatch();
 
     if (replaceCount)
@@ -286,49 +359,44 @@ PlacesTreeView.prototype = {
     }
 
     this._tree.endUpdateBatch();
 
     // restore selection
     if (previouslySelectedNodes.length > 0) {
       for (var i = 0; i < previouslySelectedNodes.length; i++) {
         var nodeInfo = previouslySelectedNodes[i];
-        var index = nodeInfo.node._viewIndex;
+        var index = nodeInfo.node.viewIndex;
 
-        // If the nodes under the invalidated container were preserved, we can
-        // just use viewIndex.
-        if (index == -1) {
-          // Otherwise, try to find an equal node.
+        // if the same node was used (happens on sorting-changes),
+        // just use viewIndex
+        if (index == -1) { // otherwise, try to find an equal node
           var itemId = PlacesUtils.getConcreteItemId(nodeInfo.node);
-          if (itemId != 1) {
-            // Search by itemId.
+          if (itemId != 1) { // bookmark-nodes in queries case
             for (var j = 0; j < newElements.length && index == -1; j++) {
               if (PlacesUtils.getConcreteItemId(newElements[j]) == itemId)
-                index = newElements[j]._viewIndex;
+                index = newElements[j].viewIndex;
             }
           }
-          else {
-            // Search by uri.
+          else { // history nodes
             var uri = nodeInfo.node.uri;
             if (uri) {
               for (var j = 0; j < newElements.length && index == -1; j++) {
                 if (newElements[j].uri == uri)
-                  index = newElements[j]._viewIndex;
+                  index = newElements[j].viewIndex;
               }
             }
           }
         }
-
-        // Select the found node, if any.
         if (index != -1)
           selection.rangedSelect(index, index, true);
       }
 
-      // If only one node was previously selected and there's no selection now,
-      // select the node at its old viewIndex, if any.
+      // if only one node was previously selected and there's no selection now,
+      // select the node at its old-viewIndex, if any
       if (previouslySelectedNodes.length == 1 &&
           selection.getRangeCount() == 0 &&
           this._visibleElements.length > previouslySelectedNodes[0].oldIndex) {
         selection.rangedSelect(previouslySelectedNodes[0].oldIndex,
                                previouslySelectedNodes[0].oldIndex, true);
       }
     }
     selection.selectEventsSuppressed = false;
@@ -437,320 +505,268 @@ PlacesTreeView.prototype = {
         return [this.COLUMN_TYPE_TAGS, false];
       case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING:
         return [this.COLUMN_TYPE_TAGS, true];
     }
     return [this.COLUMN_TYPE_UNKNOWN, false];
   },
 
   // nsINavHistoryResultViewer
-  nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
+  itemInserted: function PTV_itemInserted(aParent, aItem, aNewIndex) {
     if (!this._tree)
       return;
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
 
-    if (PlacesUtils.nodeIsSeparator(aNode) &&
+    if (PlacesUtils.nodeIsSeparator(aItem) &&
         this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
-      aNode._viewIndex = -1;
+      aItem.viewIndex = -1;
       return;
     }
 
-    // Update parent when inserting the first item, since twisty may
-    // have changed.
-    if (aParentNode.childCount == 1)
-      this._tree.invalidateRow(aParentNode._viewIndex);
+    // update parent when inserting the first item because twisty may
+    // have changed
+    if (aParent.childCount == 1)
+      this.itemChanged(aParent);
 
     // compute the new view index of the item
     var newViewIndex = -1;
     if (aNewIndex == 0) {
       // item is the first thing in our child list, it takes our index +1. Note
       // that this computation still works if the parent is an invisible root
       // node, because root_index + 1 = -1 + 1 = 0
-      newViewIndex = aParentNode._viewIndex + 1;
+      newViewIndex = aParent.viewIndex + 1;
     }
     else {
       // Here, we try to find the next visible element in the child list so we
       // can set the new visible index to be right before that. Note that we
       // have to search DOWN instead of up, because some siblings could have
       // children themselves that would be in the way.
-      for (var i = aNewIndex + 1; i < aParentNode.childCount; i++) {
-        var viewIndex = aParentNode.getChild(i)._viewIndex;
+      for (var i = aNewIndex + 1; i < aParent.childCount; i ++) {
+        var viewIndex = aParent.getChild(i).viewIndex;
         if (viewIndex >= 0) {
           // the view indices of subsequent children have not been shifted so
           // the next item will have what should be our index
           newViewIndex = viewIndex;
           break;
         }
       }
       if (newViewIndex < 0) {
         // At the end of the child list without finding a visible sibling: This
         // is a little harder because we don't know how many rows the last item
         // in our list takes up (it could be a container with many children).
-        var prevChild = aParentNode.getChild(aNewIndex - 1);
-        newViewIndex = prevChild._viewIndex + this._countVisibleRowsForNode(prevChild);
+        var prevChild = aParent.getChild(aNewIndex - 1);
+        newViewIndex = prevChild.viewIndex + this._countVisibleRowsForItem(prevChild);
       }
     }
 
-    aNode._viewIndex = newViewIndex;
-    this._visibleElements.splice(newViewIndex, 0, aNode);
+    aItem.viewIndex = newViewIndex;
+    this._visibleElements.splice(newViewIndex, 0, 
+                                 { node: aItem, properties: null });
     for (var i = newViewIndex + 1;
-         i < this._visibleElements.length; i++) {
-      this._visibleElements[i]._viewIndex = i;
+         i < this._visibleElements.length; i ++) {
+      this._visibleElements[i].node.viewIndex = i;
     }
     this._tree.rowCountChanged(newViewIndex, 1);
 
-    if (PlacesUtils.nodeIsContainer(aNode) && asContainer(aNode).containerOpen)
-      this._refreshVisibleSection(aNode);
+    // Need to redraw the rows around this one because session boundaries
+    // may have changed. For example, if we add a page to a session, the
+    // previous page will need to be redrawn because its session border
+    // will disappear.
+    if (this._showSessions) {
+      if (newViewIndex > 0)
+        this._tree.invalidateRange(newViewIndex - 1, newViewIndex - 1);
+      if (newViewIndex < this._visibleElements.length -1)
+        this._tree.invalidateRange(newViewIndex + 1, newViewIndex + 1);
+    }
+
+    if (PlacesUtils.nodeIsContainer(aItem) && asContainer(aItem).containerOpen)
+      this._refreshVisibleSection(aItem);
   },
 
-  // This is used in nodeRemoved and nodeMoved to fix viewIndex values.
-  // Throws if the node has an invalid viewIndex.
-  _fixViewIndexOnRemove: function PTV_fixViewIndexOnRemove(aNode,
-                                                           aParentNode) {
-    var oldViewIndex = aNode._viewIndex;
+  // this is used in itemRemoved and itemMoved to fix viewIndex values
+  // throw if the item has an invalid viewIndex
+  _fixViewIndexOnRemove: function PTV_fixViewIndexOnRemove(aItem, aParent) {
+    var oldViewIndex = aItem.viewIndex;
     // this may have been a container, in which case it has a lot of rows
-    var count = this._countVisibleRowsForNode(aNode);
+    var count = this._countVisibleRowsForItem(aItem);
 
     if (oldViewIndex > this._visibleElements.length)
-      throw("Trying to remove a node with an invalid viewIndex");
+      throw("Trying to remove an item with an invalid viewIndex");
 
     this._visibleElements.splice(oldViewIndex, count);
     for (var i = oldViewIndex; i < this._visibleElements.length; i++)
-      this._visibleElements[i]._viewIndex = i;
+      this._visibleElements[i].node.viewIndex = i;
 
     this._tree.rowCountChanged(oldViewIndex, -count);
 
     // redraw parent because twisty may have changed
-    if (!aParentNode.hasChildren)
-      this._tree.invalidateRow(aParentNode._viewIndex);
+    if (!aParent.hasChildren)
+      this.itemChanged(aParent);
+
+    return;
   },
 
   /**
    * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
    * removed but the node it is collapsed with is not being removed (this then
    * just swap out the removee with its collapsing partner). The only time
    * when we really remove things is when deleting URIs, which will apply to
    * all collapsees. This function is called sometimes when resorting items.
    * However, we won't do this when sorted by date because dates will never
    * change for visits, and date sorting is the only time things are collapsed.
    */
-  nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) {
+  itemRemoved: function PTV_itemRemoved(aParent, aItem, aOldIndex) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
     if (!this._tree)
       return; // nothing to do
 
-    var oldViewIndex = aNode._viewIndex;
-    if (oldViewIndex < 0) {
-      // There's nothing to do if the node was already invisible.
-      return;
-    }
+    var oldViewIndex = aItem.viewIndex;
+    if (oldViewIndex < 0)
+      return; // item was already invisible, nothing to do
 
-    // If the node was exclusively selected, the node next to it will be
-    // selected.
+    // if the item was exclusively selected, the node next to it will be
+    // selected
     var selectNext = false;
     var selection = this.selection;
     if (selection.getRangeCount() == 1) {
       var min = { }, max = { };
       selection.getRangeAt(0, min, max);
       if (min.value == max.value &&
-          this.nodeForTreeIndex(min.value) == aNode)
+          this.nodeForTreeIndex(min.value) == aItem)
         selectNext = true;
     }
 
-    // Remove the node and fix viewIndex values.
-    this._fixViewIndexOnRemove(aNode, aParentNode);
+    // remove the item and fix viewIndex values
+    this._fixViewIndexOnRemove(aItem, aParent);
 
-    // Restore selection if the node was exclusively selected.
+    // restore selection if the item was exclusively selected
     if (!selectNext)
       return;
-
-    // Restore selection
+    // restore selection
     if (this._visibleElements.length > oldViewIndex)
       selection.rangedSelect(oldViewIndex, oldViewIndex, true);    
     else if (this._visibleElements.length > 0) {
       // if we removed the last child, we select the new last child if exists
       selection.rangedSelect(this._visibleElements.length - 1,
                              this._visibleElements.length - 1, true);
     }
   },
 
   /**
    * Be careful, aOldIndex and aNewIndex specify the index in the
    * corresponding parent nodes, not the visible indexes.
    */
-  nodeMoved:
-  function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) {
+  itemMoved:
+  function PTV_itemMoved(aItem, aOldParent, aOldIndex, aNewParent, aNewIndex) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
     if (!this._tree)
       return; // nothing to do
 
-    var oldViewIndex = aNode._viewIndex;
-    if (oldViewIndex < 0) {
-      // There's nothing to do if the node was already invisible.
-      return;
-    }
+    var oldViewIndex = aItem.viewIndex;
+    if (oldViewIndex < 0)
+      return; // item was already invisible, nothing to do
 
-    // This may have been a container, in which case it has a lot of rows.
-    var count = this._countVisibleRowsForNode(aNode);
+    // this may have been a container, in which case it has a lot of rows
+    var count = this._countVisibleRowsForItem(aItem);
 
-    // Persist selection state.
+    // Persist selection state
     var nodesToSelect = [];
     var selection = this.selection;
     var rc = selection.getRangeCount();
     for (var rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
       var min = { }, max = { };
       selection.getRangeAt(rangeIndex, min, max);
       var lastIndex = Math.min(max.value, oldViewIndex + count -1);
       if (min.value < oldViewIndex || min.value > lastIndex)
         continue;
 
       for (var nodeIndex = min.value; nodeIndex <= lastIndex; nodeIndex++)
-        nodesToSelect.push(this._visibleElements[nodeIndex]);
+        nodesToSelect.push(this._visibleElements[nodeIndex].node);
     }
     if (nodesToSelect.length > 0)
       selection.selectEventsSuppressed = true;
 
-    // Remove node from the old position.
-    this._fixViewIndexOnRemove(aNode, aOldParent);
+    // remove item from the old position
+    this._fixViewIndexOnRemove(aItem, aOldParent);
 
-    // Insert the node into the new position.
-    this.nodeInserted(aNewParent, aNode, aNewIndex);
+    // insert the item into the new position
+    this.itemInserted(aNewParent, aItem, aNewIndex);
 
-    // Restore selection.
+    // restore selection
     if (nodesToSelect.length > 0) {
       for (var i = 0; i < nodesToSelect.length; i++) {
         var node = nodesToSelect[i];
-        var index = node._viewIndex;
+        var index = node.viewIndex;
         selection.rangedSelect(index, index, true);
       }
       selection.selectEventsSuppressed = false;
     }
   },
 
   /**
-   * Be careful, the parameter 'aIndex' here specifies the node's index in the
-   * parent node, not the visible index.
+   * Be careful, the parameter 'aIndex' here specifies the index in the parent
+   * node of the item, not the visible index.
+   *
+   * This is called from the result when the item is replaced, but this object
+   * calls this function internally also when duplicate collapsing changes. In
+   * this case, aIndex will be 0, so we should be careful not to use the value.
    */
-  nodeReplaced:
-  function PTV_nodeReplaced(aParentNode, aOldNode, aNewNode, aIndexDoNotUse) {
+  itemReplaced:
+  function PTV_itemReplaced(aParent, aOldItem, aNewItem, aIndexDoNotUse) {
     if (!this._tree)
       return;
 
-    var viewIndex = aOldNode._viewIndex;
-    aNewNode._viewIndex = viewIndex;
+    var viewIndex = aOldItem.viewIndex;
+    aNewItem.viewIndex = viewIndex;
     if (viewIndex >= 0 &&
         viewIndex < this._visibleElements.length) {
-      this._visibleElements[viewIndex] = aNewNode;
+      this._visibleElements[viewIndex].node = aNewItem;
+      this._visibleElements[viewIndex].properties = null;
     }
-    aOldNode._viewIndex = -1;
+    aOldItem.viewIndex = -1;
     this._tree.invalidateRow(viewIndex);
   },
 
-  _invalidateCellValue: function PTV__invalidateCellValue(aNode,
-                                                          aColumnType) {
+  itemChanged: function PTV_itemChanged(aItem) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
-    let viewIndex = aNode._viewIndex;
-    if (viewIndex == -1) // invisible
-      return;
-
-    if (this._tree) {
-      let column = this._findColumnByType(aColumnType);
-      if (column && !column.element.hidden)
-        this._tree.invalidateCell(viewIndex, column);
-
-      // Last modified time is altered for almost all node changes.
-      if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
-        let lastModifiedColumn =
-          this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
-        if (lastModifiedColumn && !lastModifiedColumn.hidden)
-          this._tree.invalidateCell(viewIndex, lastModifiedColumn);
-      }
-    }
-  },
-
-  nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
-  },
-
-  nodeURIChanged: function PTV_nodeURIChanged(aNode, aNewURI) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
-  },
-
-  nodeIconChanged: function PTV_nodeIconChanged(aNode) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
+    var viewIndex = aItem.viewIndex;
+    if (this._tree && viewIndex >= 0)
+      this._tree.invalidateRow(viewIndex);
   },
 
-  nodeHistoryDetailsChanged:
-  function PTV_nodeHistoryDetailsChanged(aNode, aUpdatedVisitDate,
-                                         aUpdatedVisitCount) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE);
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT);
-  },
-
-  nodeTagsChanged: function PTV_nodeTagsChanged(aNode) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
-  },
-
-  nodeKeywordChanged: function PTV_nodeKeywordChanged(aNode, aNewKeyword) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_KEYWORD);
+  containerOpened: function PTV_containerOpened(aItem) {
+    this.invalidateContainer(aItem);
   },
 
-  nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
-    if (aAnno == DESCRIPTION_ANNO)
-      this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION);
-  },
-
-  nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED);
+  containerClosed: function PTV_containerClosed(aItem) {
+    this.invalidateContainer(aItem);
   },
 
-  nodeLastModifiedChanged:
-  function PTV_nodeLastModifiedChanged(aNode, aNewValue) {
-    this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED);
-  },
-
-  containerOpened: function PTV_containerOpened(aNode) {
-    this.invalidateContainer(aNode);
-  },
-
-  containerClosed: function PTV_containerClosed(aNode) {
-    this.invalidateContainer(aNode);
-  },
-
-  invalidateContainer: function PTV_invalidateContainer(aNode) {
+  invalidateContainer: function PTV_invalidateContainer(aItem) {
     NS_ASSERT(this._result, "Got a notification but have no result!");
     if (!this._tree)
       return; // nothing to do, container is not visible
-
-    if (aNode._viewIndex >= this._visibleElements.length) {
+    var viewIndex = aItem.viewIndex;
+    if (viewIndex >= this._visibleElements.length) {
       // be paranoid about visible indices since others can change it
       throw Cr.NS_ERROR_UNEXPECTED;
     }
-    this._refreshVisibleSection(aNode);
+    this._refreshVisibleSection(aItem);
   },
 
-  _columns: [],
-  _findColumnByType: function PTV__findColumnByType(aColumnType) {
-    if (this._columns[aColumnType])
-      return this._columns[aColumnType];
+  invalidateAll: function PTV_invalidateAll() {
+    NS_ASSERT(this._result, "Got message but don't have a result!");
+    if (!this._tree)
+      return;
 
-    var columns = this._tree.columns;
-    var colCount = columns.count;
-    for (var i = 0; i < colCount; i++) {
-      let column = columns.getColumnAt(i);
-      let columnType = this._getColumnType(column);
-      this._columns[columnType] = column;
-      if (columnType == aColumnType)
-        return column;
-    }
+    var oldRowCount = this._visibleElements.length;
 
-    // That's completely valid.  Most of our trees actually include just the
-    // title column.
-    return null;
+    // update flat list to new contents
+    this._buildVisibleList();
   },
 
   sortingChanged: function PTV__sortingChanged(aSortingMode) {
     if (!this._tree || !this._result)
       return;
 
     // depending on the sort mode, certain commands may be disabled
     window.updateCommands("sort");
@@ -760,64 +776,64 @@ PlacesTreeView.prototype = {
     // clear old sorting indicator
     var sortedColumn = columns.getSortedColumn();
     if (sortedColumn)
       sortedColumn.element.removeAttribute("sortDirection");
 
     // set new sorting indicator by looking through all columns for ours
     if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
       return;
-
     var [desiredColumn, desiredIsDescending] =
       this._sortTypeToColumnType(aSortingMode);
     var colCount = columns.count;
-    var column = this._findColumnByType(desiredColumn);
-    if (column) {
-      let sortDir = desiredIsDescending ? "descending" : "ascending";
-      column.element.setAttribute("sortDirection", sortDir);
+    for (var i = 0; i < colCount; i ++) {
+      var column = columns.getColumnAt(i);
+      if (this._getColumnType(column) == desiredColumn) {
+        // found our desired one, set
+        if (desiredIsDescending)
+          column.element.setAttribute("sortDirection", "descending");
+        else
+          column.element.setAttribute("sortDirection", "ascending");
+        break;
+      }
     }
   },
 
   get result() {
     return this._result;
   },
 
   set result(val) {
-    // Some methods (e.g. getURLsFromContainer) temporarily null out the
+    // some methods (e.g. getURLsFromContainer) temporarily null out the
     // viewer when they do temporary changes to the view, this does _not_
     // call setResult(null), but then, we're called again with the result
     // object which is already set for this viewer. At that point,
     // we should do nothing.
     if (this._result != val) {
       if (this._result)
-        this._rootNode.containerOpen = false;
-
+        this._result.root.containerOpen = false;
       this._result = val;
-      this._rootNode = val ? val.root : null;
-
-      // If the tree is not set yet, setTree will call finishInit.
-      if (this._tree && val)
-        this._finishInit();
+      this._finishInit();
     }
     return val;
   },
 
   nodeForTreeIndex: function PTV_nodeForTreeIndex(aIndex) {
     if (aIndex > this._visibleElements.length)
       throw Cr.NS_ERROR_INVALID_ARG;
 
-    return this._visibleElements[aIndex];
+    return this._visibleElements[aIndex].node;
   },
 
   treeIndexForNode: function PTV_treeNodeForIndex(aNode) {
-    var viewIndex = aNode._viewIndex;
+    var viewIndex = aNode.viewIndex;
     if (viewIndex < 0)
       return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
 
-    NS_ASSERT(this._visibleElements[viewIndex] == aNode,
+    NS_ASSERT(this._visibleElements[viewIndex].node == aNode,
               "Node's visible index and array out of sync");
     return viewIndex;
   },
 
   _getResourceForNode: function PTV_getResourceForNode(aNode)
   {
     var uri = aNode.uri;
     NS_ASSERT(uri, "if there is no uri, we can't persist the open state");
@@ -832,17 +848,35 @@ PlacesTreeView.prototype = {
   get selection() {
     return this._selection;
   },
 
   set selection(val) {
     return this._selection = val;
   },
 
-  getRowProperties: function PTV_getRowProperties(aRow, aProperties) { },
+  getRowProperties: function PTV_getRowProperties(aRow, aProperties) {
+    this._ensureValidRow(aRow);
+
+    // Handle properties for session information.
+    if (!this._showSessions)
+      return;
+
+    var status = this._getRowSessionStatus(aRow);
+    switch (status) {
+      case this.SESSION_STATUS_NONE:
+        break;
+      case this.SESSION_STATUS_START:
+        aProperties.AppendElement(this._getAtomFor("session-start"));
+        break;
+      case this.SESSION_STATUS_CONTINUE:
+        aProperties.AppendElement(this._getAtomFor("session-continue"));
+        break
+    }
+  },
 
   getCellProperties: function PTV_getCellProperties(aRow, aColumn, aProperties) {
     this._ensureValidRow(aRow);
 
     // for anonid-trees, we need to add the column-type manually
     var columnType = aColumn.element.getAttribute("anonid");
     if (columnType)
       aProperties.AppendElement(this._getAtomFor(columnType));
@@ -851,19 +885,21 @@ PlacesTreeView.prototype = {
 
     // Set the "ltr" property on url cells
     if (columnType == "url")
       aProperties.AppendElement(this._getAtomFor("ltr"));
 
     if (columnType != "title")
       return;
 
-    var node = this._visibleElements[aRow];
-    if (!node._cellProperties) {
-      let properties = new Array();
+    var node = this._visibleElements[aRow].node;
+    var properties = this._visibleElements[aRow].properties;
+
+    if (!properties) {
+      properties = new Array();
       var itemId = node.itemId;
       var nodeType = node.type;
       if (PlacesUtils.containerTypes.indexOf(nodeType) != -1) {
         if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
           properties.push(this._getAtomFor("query"));
           if (PlacesUtils.nodeIsTagQuery(node))
             properties.push(this._getAtomFor("tagContainer"));
           else if (PlacesUtils.nodeIsDay(node))
@@ -888,71 +924,82 @@ PlacesTreeView.prototype = {
       else if (PlacesUtils.nodeIsURI(node)) {
         properties.push(this._getAtomFor(PlacesUIUtils.guessUrlSchemeForUI(node.uri)));
         if (itemId != -1) {
           if (PlacesUtils.nodeIsLivemarkContainer(node.parent))
             properties.push(this._getAtomFor("livemarkItem"));
         }
       }
 
-      node._cellProperties = properties;
+      this._visibleElements[aRow].properties = properties;
     }
-    for (var i = 0; i < node._cellProperties.length; i++)
-      aProperties.AppendElement(node._cellProperties[i]);
+    for (var i = 0; i < properties.length; i++)
+      aProperties.AppendElement(properties[i]);
   },
 
   getColumnProperties: function(aColumn, aProperties) { },
 
   isContainer: function PTV_isContainer(aRow) {
     this._ensureValidRow(aRow);
 
-    var node = this._visibleElements[aRow];
+    var node = this._visibleElements[aRow].node;
     if (PlacesUtils.nodeIsContainer(node)) {
+      // the root node is always expandable
+      if (!node.parent)
+        return true;
+
       // Flat-lists may ignore expandQueries and other query options when
       // they are asked to open a container.
       if (this._flatList)
         return true;
 
       // treat non-expandable childless queries as non-containers
       if (PlacesUtils.nodeIsQuery(node)) {
         var parent = node.parent;
-        if ((PlacesUtils.nodeIsQuery(parent) ||
-             PlacesUtils.nodeIsFolder(parent)) &&
-            !node.hasChildren)
+        if((PlacesUtils.nodeIsQuery(parent) ||
+            PlacesUtils.nodeIsFolder(parent)) &&
+           !node.hasChildren)
           return asQuery(parent).queryOptions.expandQueries;
       }
       return true;
     }
     return false;
   },
 
   isContainerOpen: function PTV_isContainerOpen(aRow) {
     if (this._flatList)
       return false;
 
     this._ensureValidRow(aRow);
-    return this._visibleElements[aRow].containerOpen;
+    if (!PlacesUtils.nodeIsContainer(this._visibleElements[aRow].node))
+      throw Cr.NS_ERROR_INVALID_ARG;
+
+    return this._visibleElements[aRow].node.containerOpen;
   },
 
   isContainerEmpty: function PTV_isContainerEmpty(aRow) {
     if (this._flatList)
       return true;
 
     this._ensureValidRow(aRow);
-    return !this._visibleElements[aRow].hasChildren;
+
+    if (!PlacesUtils.nodeIsContainer(this._visibleElements[aRow].node))
+      throw Cr.NS_ERROR_INVALID_ARG;
+
+    return !this._visibleElements[aRow].node.hasChildren;
   },
 
   isSeparator: function PTV_isSeparator(aRow) {
     this._ensureValidRow(aRow);
-    return PlacesUtils.nodeIsSeparator(this._visibleElements[aRow]);
+    return PlacesUtils.nodeIsSeparator(this._visibleElements[aRow].node);
   },
 
   isSorted: function PTV_isSorted() {
     return this._result.sortingMode !=
-           Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
+           Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_NONE;
   },
 
   canDrop: function PTV_canDrop(aRow, aOrientation) {
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
 
     // drop position into a sorted treeview would be wrong
     if (this.isSorted())
@@ -973,18 +1020,18 @@ PlacesTreeView.prototype = {
         // If the last selected item is an open container, append _into_
         // it, rather than insert adjacent to it. 
         container = lastSelected;
         index = -1;
       }
       else if (lastSelected.containerOpen &&
                orientation == Ci.nsITreeView.DROP_AFTER &&
                lastSelected.hasChildren) {
-        // If the last selected node is an open container and the user is
-        // trying to drag into it as a first node, really insert into it.
+        // If the last selected item is an open container and the user is
+        // trying to drag into it as a first item, really insert into it.
         container = lastSelected;
         orientation = Ci.nsITreeView.DROP_ON;
         index = 0;
       }
       else {
         // Use the last-selected node's container unless the root node
         // is selected, in which case we use the root node itself as the
         // insertion point.
@@ -1033,65 +1080,76 @@ PlacesTreeView.prototype = {
     var ip = this._getInsertionPoint(aRow, aOrientation);
     if (!ip)
       return;
     PlacesControllerDragHelper.onDrop(ip);
   },
 
   getParentIndex: function PTV_getParentIndex(aRow) {
     this._ensureValidRow(aRow);
-    var parent = this._visibleElements[aRow].parent;
-    if (!parent || parent._viewIndex < 0)
+    var parent = this._visibleElements[aRow].node.parent;
+    if (!parent || parent.viewIndex < 0)
       return -1;
 
-    return parent._viewIndex;
+    return parent.viewIndex;
   },
 
   hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) {
     this._ensureValidRow(aRow);
     if (aRow == this._visibleElements.length -1) {
       // this is the last thing in the list -> no next sibling
       return false;
     }
 
-    var thisLevel = this._visibleElements[aRow].indentLevel;
+    var thisLevel = this._visibleElements[aRow].node.indentLevel;
     for (var i = aAfterIndex + 1; i < this._visibleElements.length; ++i) {
-      var nextLevel = this._visibleElements[i].indentLevel;
+      var nextLevel = this._visibleElements[i].node.indentLevel;
       if (nextLevel == thisLevel)
         return true;
       if (nextLevel < thisLevel)
         break;
     }
     return false;
   },
 
   getLevel: function PTV_getLevel(aRow) {
     this._ensureValidRow(aRow);
 
-    // Level is 0 for nodes at the root level, 1 for its children and so on.
-    return this._visibleElements[aRow].indentLevel;
+    // Level is 0 for items at the root level, 1 for its children and so on.
+    // If we don't show the result's root node, the level is simply the node's
+    // indentLevel; if we do, it is the node's indentLevel increased by 1.
+    // That is because nsNavHistoryResult uses -1 as the indent level for the
+    // root node regardless of our internal showRoot state.
+    if (this._showRoot)
+      return this._visibleElements[aRow].node.indentLevel + 1;
+
+    return this._visibleElements[aRow].node.indentLevel;
   },
 
   getImageSrc: function PTV_getImageSrc(aRow, aColumn) {
     this._ensureValidRow(aRow);
 
     // only the title column has an image
     if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
       return "";
 
-    return this._visibleElements[aRow].icon;
+    var node = this._visibleElements[aRow].node;
+    var icon = node.icon;
+    if (icon)
+      return icon.spec;
+    return "";
   },
 
   getProgressMode: function(aRow, aColumn) { },
   getCellValue: function(aRow, aColumn) { },
 
   getCellText: function PTV_getCellText(aRow, aColumn) {
     this._ensureValidRow(aRow);
 
-    var node = this._visibleElements[aRow];
+    var node = this._visibleElements[aRow].node;
     var columnType = this._getColumnType(aColumn);
     switch (columnType) {
       case this.COLUMN_TYPE_TITLE:
         // normally, this is just the title, but we don't want empty items in
         // the tree view so return a special string if the title is empty.
         // Do it here so that callers can still get at the 0 length title
         // if they go through the "result" API.
         if (PlacesUtils.nodeIsSeparator(node))
@@ -1099,27 +1157,27 @@ PlacesTreeView.prototype = {
         return PlacesUIUtils.getBestTitle(node);
       case this.COLUMN_TYPE_TAGS:
         return node.tags;
       case this.COLUMN_TYPE_URI:
         if (PlacesUtils.nodeIsURI(node))
           return node.uri;
         return "";
       case this.COLUMN_TYPE_DATE:
-        let nodeTime = node.time;
-        if (nodeTime == 0 || !PlacesUtils.nodeIsURI(node)) {
+        if (node.time == 0 || !PlacesUtils.nodeIsURI(node)) {
           // hosts and days shouldn't have a value for the date column.
           // Actually, you could argue this point, but looking at the
           // results, seeing the most recently visited date is not what
           // I expect, and gives me no information I know how to use.
           // Only show this for URI-based items.
           return "";
         }
-
-        return this._convertPRTimeToString(nodeTime);
+        if (this._getRowSessionStatus(aRow) != this.SESSION_STATUS_CONTINUE)
+          return this._convertPRTimeToString(node.time);
+        return "";
       case this.COLUMN_TYPE_VISITCOUNT:
         return node.accessCount;
       case this.COLUMN_TYPE_KEYWORD:
         if (PlacesUtils.nodeIsBookmark(node))
           return PlacesUtils.bookmarks.getKeywordForBookmark(node.itemId);
         return "";
       case this.COLUMN_TYPE_DESCRIPTION:
         if (node.itemId != -1) {
@@ -1141,34 +1199,37 @@ PlacesTreeView.prototype = {
     }
     return "";
   },
 
   setTree: function PTV_setTree(aTree) {
     var hasOldTree = this._tree != null;
     this._tree = aTree;
 
-    if (this._result) {
-      if (hasOldTree) {
-        // detach from result when we are detaching from the tree.
-        // This breaks the reference cycle between us and the result.
-        if (!aTree)
-          this._result.viewer = null;
-      }
-      if (aTree)
-        this._finishInit();
+    // do this before detaching from result when there is no tree.
+    // This ensures that the visible indices of the elements in the
+    // result have been set to -1
+    this._finishInit();
+
+    if (!aTree && hasOldTree && this._result) {
+      // detach from result when we are detaching from the tree.
+      // This breaks the reference cycle between us and the result.
+      this._result.viewer = null;
     }
   },
 
   toggleOpenState: function PTV_toggleOpenState(aRow) {
     if (!this._result)
       throw Cr.NS_ERROR_UNEXPECTED;
     this._ensureValidRow(aRow);
 
-    var node = this._visibleElements[aRow];
+    var node = this._visibleElements[aRow].node;
+    if (!PlacesUtils.nodeIsContainer(node))
+      return; // not a container, nothing to do
+
     if (this._flatList && this._openContainerCallback) {
       this._openContainerCallback(node);
       return;
     }
 
     var resource = this._getResourceForNode(node);
     if (resource) {
       const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
@@ -1335,16 +1396,21 @@ PlacesTreeView.prototype = {
   selectionChanged: function() { },
   cycleCell: function PTV_cycleCell(aRow, aColumn) { },
   isSelectable: function(aRow, aColumn) { return false; },
   performAction: function(aAction) { },
   performActionOnRow: function(aAction, aRow) { },
   performActionOnCell: function(aAction, aRow, aColumn) { }
 };
 
-function PlacesTreeView(aFlatList, aOnOpenFlatContainer) {
+function PlacesTreeView(aShowRoot, aFlatList, aOnOpenFlatContainer) {
+  if (aShowRoot && aFlatList)
+    throw("Flat-list mode is not supported when show-root is set");
+
   this._tree = null;
   this._result = null;
+  this._showSessions = false;
   this._selection = null;
   this._visibleElements = [];
+  this._showRoot = aShowRoot;
   this._flatList = aFlatList;
   this._openContainerCallback = aOnOpenFlatContainer;
 }
--- a/browser/components/places/content/utils.js
+++ b/browser/components/places/content/utils.js
@@ -997,22 +997,27 @@ var PlacesUIUtils = {
   guessUrlSchemeForUI: function PUU_guessUrlSchemeForUI(aUrlString) {
     return aUrlString.substr(0, aUrlString.indexOf(":"));
   },
 
   /**
    * Helper for the toolbar and menu views
    */
   createMenuItemForNode:
-  function PUU_createMenuItemForNode(aNode) {
+  function PUU_createMenuItemForNode(aNode, aContainersMap) {
     var element;
     var type = aNode.type;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
       element = document.createElement("menuseparator");
     else {
+      var iconURI = aNode.icon;
+      var iconURISpec = "";
+      if (iconURI)
+        iconURISpec = iconURI.spec;
+
       if (PlacesUtils.uriTypes.indexOf(type) != -1) {
         element = document.createElement("menuitem");
         element.className = "menuitem-iconic bookmark-item";
         element.setAttribute("scheme", this.guessUrlSchemeForUI(aNode.uri));
       }
       else if (PlacesUtils.containerTypes.indexOf(type) != -1) {
         element = document.createElement("menu");
         element.setAttribute("container", "true");
@@ -1041,29 +1046,30 @@ var PlacesUIUtils = {
         // the popup correctly.
         popup._startMarker = -1;
         popup._endMarker = -1;
 #else
         // no context menu on mac
         popup.setAttribute("context", "placesContext");
 #endif
         element.appendChild(popup);
+        if (aContainersMap)
+          aContainersMap.push({ resultNode: aNode, domNode: popup });
         element.className = "menu-iconic bookmark-item";
       }
       else
         throw "Unexpected node";
 
       element.setAttribute("label", this.getBestTitle(aNode));
 
-      var icon = aNode.icon;
-      if (icon)
-        element.setAttribute("image", icon);
+      if (iconURISpec)
+        element.setAttribute("image", iconURISpec);
     }
     element.node = aNode;
-    element.node._DOMElement = element;
+    element.node.viewIndex = 0;
 
     return element;
   },
 
   cleanPlacesPopup: function PU_cleanPlacesPopup(aPopup) {
     // Remove places popup children and update markers to keep track of
     // their indices.
     var start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
--- a/browser/components/places/src/nsPlacesTransactionsService.js
+++ b/browser/components/places/src/nsPlacesTransactionsService.js
@@ -570,17 +570,17 @@ placesCreateLivemarkTransactions.prototy
       PlacesUtils.bookmarks.setItemGUID(this._id, this._GUID);
   },
 
   undoTransaction: function PCLT_undoTransaction() {
     // If a GUID exists for this item, preserve it before removing the item.
     if (PlacesUtils.annotations.itemHasAnnotation(this._id, GUID_ANNO))
       this._GUID = PlacesUtils.bookmarks.getItemGUID(this._id);
 
-    PlacesUtils.bookmarks.removeItem(this._id);
+    PlacesUtils.bookmarks.removeFolder(this._id);
   }
 };
 
 function placesRemoveLivemarkTransaction(aFolderId) {
   this.redoTransaction = this.doTransaction;
   this._id = aFolderId;
   this._title = PlacesUtils.bookmarks.getItemTitle(this._id);
   this._container = PlacesUtils.bookmarks.getFolderIdForItem(this._id);
--- a/browser/components/places/tests/browser/browser_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_views_liveupdate.js
@@ -183,48 +183,45 @@ var bookmarksObserver = {
   QueryInterface: function PSB_QueryInterface(aIID) {
     if (aIID.equals(Ci.nsINavBookmarkObserver) ||
         aIID.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_NOINTERFACE;
   },
 
   // nsINavBookmarkObserver
-  onItemAdded: function PSB_onItemAdded(aItemId, aFolderId, aIndex,
-                                        aItemType) {
+  onItemAdded: function PSB_onItemAdded(aItemId, aFolderId, aIndex) {
     var views = getViewsForFolder(aFolderId);
     ok(views.length > 0, "Found affected views: " + views);
 
     // Check that item has been added in the correct position.
     for (var i = 0; i < views.length; i++) {
       var node = null;
       var index = null;
       [node, index] = searchItemInView(aItemId, views[i]);
       isnot(node, null, "Found new Places node in " + views[i]);
       is(index, aIndex, "Node is at index " + index);
     }
   },
 
-  onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex,
-                                            aItemType) {
+  onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex) {
     var views = getViewsForFolder(aFolderId);
     ok(views.length > 0, "Found affected views: " + views);
     // Check that item has been removed.
     for (var i = 0; i < views.length; i++) {
       var node = null;
       var index = null;
       [node, index] = searchItemInView(aItemId, views[i]);
       is(node, null, "Places node not found in " + views[i]);
     }
   },
 
   onItemMoved: function(aItemId,
                         aOldFolderId, aOldIndex,
-                        aNewFolderId, aNewIndex,
-                        aItemType) {
+                        aNewFolderId, aNewIndex) {
     var views = getViewsForFolder(aNewFolderId);
     ok(views.length > 0, "Found affected views: " + views);
 
     // Check that item has been moved in the correct position.
     for (var i = 0; i < views.length; i++) {
       var node = null;
       var index = null;
       [node, index] = searchItemInView(aItemId, views[i]);
@@ -232,17 +229,18 @@ var bookmarksObserver = {
       is(index, aNewIndex, "Node is at index " + index);
     }
   },
 
   onBeginUpdateBatch: function PSB_onBeginUpdateBatch() {},
   onEndUpdateBatch: function PSB_onEndUpdateBatch() {},
   onBeforeItemRemoved: function PSB_onBeforeItemRemoved(aItemId) {},
   onItemVisited: function() {},
-  onItemChanged: function PSB_onItemChanged() {}
+  onItemChanged: function PSB_onItemChanged(aItemId, aProperty,
+                                            aIsAnnotationProperty, aValue) {}
 };
 
 /**
  * Search an item id in a view.
  *
  * @param aItemId
  *        item id of the item to search.
  * @param aView
@@ -288,17 +286,17 @@ function getNodeForToolbarItem(aItemId) 
       if (child.node.itemId == aItemId)
         return [child.node, i - staticNodes];
 
       // Don't search in queries, they could contain our item in a
       // different position.  Search only folders
       if (PlacesUtils.nodeIsFolder(child.node)) {
         var popup = child.lastChild;
         popup.showPopup(popup);
-        var foundNode = findNode(popup);
+        foundNode = findNode(popup);
         popup.hidePopup();
         if (foundNode[0] != null)
           return foundNode;
       }
     }
     return [null, null];
   }
 
@@ -331,17 +329,17 @@ function getNodeForMenuItem(aItemId) {
 
       // Don't search in queries, they could contain our item in a
       // different position.  Search only folders
       if (PlacesUtils.nodeIsFolder(child.node)) {
         var popup = child.lastChild;
         // XXX Why is this needed on Linux and Mac?
         popup.showPopup(popup);
         child.open = true;
-        var foundNode = findNode(popup);
+        foundNode = findNode(popup);
         popup.hidePopup();
         child.open = false;
         if (foundNode[0] != null)
           return foundNode;
       }
     }
     return [null, null];
   }
--- a/browser/components/places/tests/unit/test_placesTxn.js
+++ b/browser/components/places/tests/unit/test_placesTxn.js
@@ -86,43 +86,40 @@ try {
 // create and add bookmarks observer
 var observer = {
   onBeginUpdateBatch: function() {
     this._beginUpdateBatch = true;
   },
   onEndUpdateBatch: function() {
     this._endUpdateBatch = true;
   },
-  onItemAdded: function(id, folder, index, itemType) {
+  onItemAdded: function(id, folder, index) {
     this._itemAddedId = id;
     this._itemAddedParent = folder;
     this._itemAddedIndex = index;
-    this._itemAddedType = itemType;
   },
   onBeforeItemRemoved: function(id) {
   },
-  onItemRemoved: function(id, folder, index, itemType) {
+  onItemRemoved: function(id, folder, index) {
     this._itemRemovedId = id;
     this._itemRemovedFolder = folder;
     this._itemRemovedIndex = index;
   },
-  onItemChanged: function(id, property, isAnnotationProperty, newValue,
-                          lastModified, itemType) {
+  onItemChanged: function(id, property, isAnnotationProperty, value) {
     this._itemChangedId = id;
     this._itemChangedProperty = property;
     this._itemChanged_isAnnotationProperty = isAnnotationProperty;
-    this._itemChangedValue = newValue;
+    this._itemChangedValue = value;
   },
   onItemVisited: function(id, visitID, time) {
     this._itemVisitedId = id;
     this._itemVisitedVistId = visitID;
     this._itemVisitedTime = time;
   },
-  onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex,
-                        itemType) {
+  onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex) {
     this._itemMovedId = id
     this._itemMovedOldParent = oldParent;
     this._itemMovedOldIndex = oldIndex;
     this._itemMovedNewParent = newParent;
     this._itemMovedNewIndex = newIndex;
   },
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsINavBookmarkObserver) ||
@@ -148,30 +145,28 @@ function run_test() {
   var annos = [{ name: DESCRIPTION_ANNO,
                  type: annosvc.TYPE_STRING,
                 flags: 0,
                 value: TEST_DESCRIPTION,
               expires: annosvc.EXPIRE_NEVER }];
   var txn1 = ptSvc.createFolder("Testing folder", root, bmStartIndex, annos);
   ptSvc.doTransaction(txn1);
 
-  // This checks that calling undoTransaction on an "empty batch" doesn't
-  // undo the previous transaction (getItemTitle will fail)
+  // the check check that calling undoTransaction on an "empty batch" doesn't undo
+  // the previous transaction
   ptSvc.beginBatch();
   ptSvc.endBatch();
   ptSvc.undoTransaction();
 
-  var folderId = observer._itemAddedId;
-  do_check_eq(bmsvc.getItemTitle(folderId), "Testing folder");
+  var folderId = bmsvc.getChildFolder(root, "Testing folder");
+  do_check_eq(TEST_DESCRIPTION, 
+              annosvc.getItemAnnotation(folderId, DESCRIPTION_ANNO));
   do_check_eq(observer._itemAddedIndex, bmStartIndex);
   do_check_eq(observer._itemAddedParent, root);
   do_check_eq(observer._itemAddedId, folderId);
-  do_check_eq(TEST_DESCRIPTION, 
-              annosvc.getItemAnnotation(folderId, DESCRIPTION_ANNO));
-
   txn1.undoTransaction();
   do_check_eq(observer._itemRemovedId, folderId);
   do_check_eq(observer._itemRemovedFolder, root);
   do_check_eq(observer._itemRemovedIndex, bmStartIndex);
   txn1.redoTransaction();
   do_check_eq(observer._itemAddedIndex, bmStartIndex);
   do_check_eq(observer._itemAddedParent, root);
   do_check_eq(observer._itemAddedId, folderId);
@@ -201,19 +196,17 @@ function run_test() {
   txn2.undoTransaction();
   do_check_eq(observer._itemRemovedId, newId);
   do_check_eq(observer._itemRemovedFolder, root);
   do_check_eq(observer._itemRemovedIndex, bmStartIndex);
 
   // Create item to a folder
   var txn2a = ptSvc.createFolder("Folder", root, bmStartIndex);
   ptSvc.doTransaction(txn2a);
-  var fldrId = observer._itemAddedId;
-  do_check_eq(bmsvc.getItemTitle(fldrId), "Folder");
-
+  var fldrId = bmsvc.getChildFolder(root, "Folder");
   var txn2b = ptSvc.createItem(uri("http://www.example2.com"), fldrId, bmStartIndex, "Testing1b");
   ptSvc.doTransaction(txn2b);
   var b2 = (bmsvc.getBookmarkIdsForURI(uri("http://www.example2.com"), {}))[0];
   do_check_eq(observer._itemAddedId, b2);
   do_check_eq(observer._itemAddedIndex, bmStartIndex);
   do_check_true(bmsvc.isBookmarked(uri("http://www.example2.com")));
   txn2b.undoTransaction();
   do_check_eq(observer._itemRemovedId, b2);
@@ -289,19 +282,17 @@ function run_test() {
   do_check_eq(observer._itemMovedId, bkmk1Id);
   do_check_eq(observer._itemMovedOldParent, fldrId);
   do_check_eq(observer._itemMovedOldIndex, 1);
   do_check_eq(observer._itemMovedNewParent, root);
   do_check_eq(observer._itemMovedNewIndex, 1);
 
   // Test Removing a Folder
   ptSvc.doTransaction(ptSvc.createFolder("Folder2", root, -1));
-  var fldrId2 = observer._itemAddedId;
-  do_check_eq(bmsvc.getItemTitle(fldrId2), "Folder2");
-
+  var fldrId2 = bmsvc.getChildFolder(root, "Folder2");
   var txn4 = ptSvc.removeItem(fldrId2);
   txn4.doTransaction();
   do_check_eq(observer._itemRemovedId, fldrId2);
   do_check_eq(observer._itemRemovedFolder, root);
   do_check_eq(observer._itemRemovedIndex, 3);
   txn4.undoTransaction();
   do_check_eq(observer._itemAddedId, fldrId2);
   do_check_eq(observer._itemAddedParent, root);
@@ -553,18 +544,17 @@ function run_test() {
   do_check_true(annosvc.pageHasAnnotation(uri("http://www.mozilla.org/"), "testAnno/testInt"));
   genPageAnnoTxn.undoTransaction();
   do_check_false(annosvc.pageHasAnnotation(uri("http://www.mozilla.org/"), "testAnno/testInt"));
   genPageAnnoTxn.redoTransaction();
   do_check_true(annosvc.pageHasAnnotation(uri("http://www.mozilla.org/"), "testAnno/testInt"));
 
   // sortFolderByName
   ptSvc.doTransaction(ptSvc.createFolder("Sorting folder", root, bmStartIndex, [], null));
-  var srtFldId = observer._itemAddedId;
-  do_check_eq(bmsvc.getItemTitle(srtFldId), "Sorting folder");
+  var srtFldId = bmsvc.getChildFolder(root, "Sorting folder");
   ptSvc.doTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "c"));
   ptSvc.doTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "b"));   
   ptSvc.doTransaction(ptSvc.createItem(uri("http://www.sortingtest.com"), srtFldId, -1, "a"));
   var b = bmsvc.getBookmarkIdsForURI(uri("http://www.sortingtest.com"), {});
   b.sort();
   var b1 = b[0];
   var b2 = b[1];
   var b3 = b[2];
--- a/browser/fuel/src/fuelApplication.js
+++ b/browser/fuel/src/fuelApplication.js
@@ -542,17 +542,17 @@ BookmarkFolder.prototype = {
 
   addFolder : function bmf_addfolder(aTitle) {
     var newFolderID = Utilities.bookmarks.createFolder(this._id, aTitle, Utilities.bookmarks.DEFAULT_INDEX);
     var newFolder = new BookmarkFolder(newFolderID, this);
     return newFolder;
   },
 
   remove : function bmf_remove() {
-    Utilities.bookmarks.removeItem(this._id);
+    Utilities.bookmarks.removeFolder(this._id);
   },
 
   // observer
   onBeginUpdateBatch : function bmf_obub() {
   },
 
   onEndUpdateBatch : function bmf_oeub() {
   },
--- a/toolkit/components/places/public/nsINavBookmarksService.idl
+++ b/toolkit/components/places/public/nsINavBookmarksService.idl
@@ -52,17 +52,17 @@ interface nsITransaction;
 interface nsINavHistoryBatchCallback;
 
 [ptr] native PRInt64Array(nsTArray<PRInt64>);
 
 /**
  * Observer for bookmark changes.
  */
 
-[scriptable, uuid(1f7e9032-b2c0-4561-b35b-94ba3f8344e2)]
+[scriptable, uuid(a3544e1e-36a8-404a-9b30-918abf8e005e)]
 interface nsINavBookmarkObserver : nsISupports
 {
   /**
    * Notify this observer that a batch transaction has started.
    * Other notifications will be sent during the batch change,
    * but the observer is guaranteed that onEndUpdateBatch() will be called
    * at the completion of changes.
    */
@@ -75,94 +75,73 @@ interface nsINavBookmarkObserver : nsISu
 
   /**
    * Notify this observer that an item was added.  Called after the actual
    * add took place. The items following the index will be shifted down, but
    * no additional notifications will be sent.
    *
    * @param aItemId
    *        The id of the bookmark that was added.
-   * @param aParentId
-   *        The id of the folder to which the item was added.
+   * @param aFolder
+   *        The folder that the item was added to.
    * @param aIndex
    *        The item's index in the folder.
-   * @param aItemType
-   *        The type of the item that was added (one of the TYPE_* constants
-   *        defined above).
    */
-  void onItemAdded(in long long aItemId, in long long aParentId,
-                   in long aIndex, in unsigned short aItemType);
+  void onItemAdded(in long long aItemId, in long long aFolder,
+                   in long aIndex);
 
   /**
    * Notify this observer that an item is about to be removed.  Called before
    * the actual removal will take place.
    *
    * @param aItemId
    *        The id of the bookmark to be removed.
-   * @param aItemType
-   *        The type of the item to be removed (one of the TYPE_* constants
-   *        defined above).
    */
-  void onBeforeItemRemoved(in long long aItemId, in unsigned short aItemType);
+  void onBeforeItemRemoved(in long long aItemId);
 
   /**
    * Notify this observer that an item was removed.  Called after the actual
    * remove took place. The items following the index will be shifted up, but
    * no additional notifications will be sent.
    *
    * @param aItemId
-   *        The id of the item that was removed.
-   * @param aParentId
-   *        The id of the folder from which the item was removed.
+   *        The id of the bookmark that was removed.
+   * @param aFolder
+   *        The folder that the item was removed from.
    * @param aIndex
    *        The bookmark's index in the folder.
-   * @param aItemType
-   *        The type of the item that was removed (one of the TYPE_* constants
-   *        defined above).
    */
-  void onItemRemoved(in long long aItemId, in long long aParentId,
-                     in long aIndex, in unsigned short aItemType);
+  void onItemRemoved(in long long aItemId, in long long aFolder,
+                     in long aIndex);
 
   /**
    * Notify this observer that an item's information has changed.  This
    * will be called whenever any attributes like "title" are changed.
    * 
    * @param aItemId
    *        The id of the item that was changed.
    * @param aProperty
    *        The property which changed.
    * @param aIsAnnotationProperty
-   *        Whether or not aProperty the name of an item annotation.
-   * @param aProperty
-   *        The property which has been changed (see list below).
-   * @param aNewValue
-   *        For certain properties, this is set to the new value of the
-   *        property (see list below).
-   * @param aLastModified
-   *        If the item's lastModified field has changed, this parameter is
-   *        set to the new value, otherwise it's set to 0.
-   * @param aItemType
-   *        The type of the item that has been changed(one of the TYPE_* constants
-   *        defined above).
-   *        
+   *        Is aProperty the name of an item annotation
+   *
    * property = "cleartime": (history was deleted, there is no last visit date):
    *                         value = empty string.
    * property = "title": value = new title.
    * property = "favicon": value = new "moz-anno" URL of favicon image
    * property = "uri": value = new uri spec.
    * property = "tags: (tags set for the bookmarked uri have changed)
    *             value = empty string.
    * property = "dateAdded": value = PRTime when the item was first added
    * property = "lastModified": value = PRTime when the item was last modified
    * aIsAnnotationProperty = true: value = empty string.
    */
-  void onItemChanged(in long long aItemId, in ACString aProperty,
+  void onItemChanged(in long long aBookmarkId, in ACString aProperty,
                      in boolean aIsAnnotationProperty,
-                     in AUTF8String aNewValue, in PRTime aLastModified,
-                     in unsigned short aItemType);
+                     in AUTF8String aValue);
 
   /**
    * Notify that the item was visited. Normally in bookmarks we use the last
    * visit date, and normally the time will be a new visit that will be more
    * recent, but this is not guaranteed. You should check to see if it's
    * actually more recent before using this new time.
    *
    * @param aBookmarkId
@@ -172,32 +151,28 @@ interface nsINavBookmarkObserver : nsISu
    */
   void onItemVisited(in long long aBookmarkId, in long long aVisitID,
                      in PRTime time);
 
   /**
    * Notify this observer that an item has been moved.
    *  @param aItemId
    *         The id of the item that was moved.
-   *  @param aOldParentId
+   *  @param aOldParent
    *         The id of the old parent.
    *  @param aOldIndex
-   *         The old index inside the old parent.
-   *  @param aNewParentId
+   *         The old index inside aOldParent.
+   *  @param aNewParent
    *         The id of the new parent.
    *  @param aNewIndex
-   *         The index inside the new parent.
-   * @param  aItemType
-   *         The type of the item that was moved (one of the TYPE_* constants
-   *         defined above).
+   *         The foindex inside aNewParent.
    */
   void onItemMoved(in long long aItemId,
-                   in long long aOldParentId, in long aOldIndex,
-                   in long long aNewParentId, in long aNewIndex,
-                   in unsigned short aItemType);
+                   in long long aOldParent, in long aOldIndex,
+                   in long long aNewParent, in long aNewIndex);
 };
 
 /**
  * The BookmarksService interface provides methods for managing bookmarked
  * history items.  Bookmarks consist of a set of user-customizable
  * folders.  A URI in history can be contained in one or more such folders.
  */
 
@@ -239,27 +214,27 @@ interface nsINavBookmarksService : nsISu
   const unsigned short TYPE_BOOKMARK = 1;
   const unsigned short TYPE_FOLDER = 2;
   const unsigned short TYPE_SEPARATOR = 3;
   const unsigned short TYPE_DYNAMIC_CONTAINER = 4;
 
   /**
    * Inserts a child bookmark into the given folder.
    *
-   *  @param aParentId
+   *  @param aParentFolder
    *         The id of the parent folder
    *  @param aURI
    *         The URI to insert
    *  @param aIndex
    *         The index to insert at, or DEFAULT_INDEX to append
    *  @param aTitle
    *         The title for the new bookmark
    *  @return The ID of the newly-created bookmark.
    */
-  long long insertBookmark(in long long aParentId, in nsIURI aURI,
+  long long insertBookmark(in long long aParentFolder, in nsIURI aURI,
                            in long aIndex, in AUTF8String aTitle);
 
   /**
    * Removes a child item. Used to delete a bookmark or separator.
    *  @param aItemId
    *         The child item to remove
    */
   void removeItem(in long long aItemId);
@@ -291,77 +266,108 @@ interface nsINavBookmarksService : nsISu
    *         The index to insert at, or DEFAULT_INDEX to append
    *         
    *  @return The ID of the newly-inserted folder.
    */
   long long createDynamicContainer(in long long aParentFolder, in AUTF8String aName,
                                    in AString aContractId, in long aIndex);
 
   /**
+   * Removes a folder from the bookmarks tree.
+   *
+   * NOTE: This API is deprecated. The correct method to use is removeItem.
+   *       This will be removed in the next release after Firefox 3.0. The
+   *       removal is in bug 428558.
+   *
+   *  @param aFolder
+   *         The id of the folder to remove.
+   */
+  void removeFolder(in long long aFolder);
+
+  /**
    * Gets an undo-able transaction for removing a folder from the bookmarks
    * tree. 
-   *  @param aItemId
+   *  @param folder
    *         The id of the folder to remove.
    *  @return An object implementing nsITransaction that can be used to undo 
    *          or redo the action. 
    *
    * This method exists because complex delete->undo operations rely on 
    * recreated folders to have the same ID they had before they were deleted, 
    * so that any other items deleted in different transactions can be 
    * re-inserted correctly. This provides a safe encapsulation of this 
    * functionality without exposing the ability to recreate folders with 
    * specific IDs (potentially dangerous if abused by other code!) in the
    * public API.
    */
-  nsITransaction getRemoveFolderTransaction(in long long aItemId);
+  nsITransaction getRemoveFolderTransaction(in long long aFolder);
 
   /**
    * Convenience function for container services.  Removes
    * all children of the given folder.
-   *  @param aItemId
+   *  @param aFolder
    *         The id of the folder to remove children from.
    */
-  void removeFolderChildren(in long long aItemId);
+  void removeFolderChildren(in long long aFolder);
 
   /**
    * Moves an item to a different container, preserving its contents.
    *  @param aItemId
    *         The id of the item to move
-   *  @param aNewParentId
+   *  @param aNewParent
    *         The id of the new parent
    *  @param aIndex
    *         The index under aNewParent, or DEFAULT_INDEX to append
    *
    * NOTE: When moving down in the same container we take into account the
    * removal of the original item. If you want to move from index X to
    * index Y > X you must use moveItem(id, folder, Y + 1)
    */
-  void moveItem(in long long aItemId, in long long aNewParentId, in long aIndex);
+  void moveItem(in long long aItemId, in long long aNewParent, in long aIndex);
+
+  /**
+   * Returns the ID of a child folder with the given name. This does not
+   * recurse, you have to give it an immediate sibling of the given folder.
+   * If the given subfolder doesn't exist, it will return 0.
+   *  @param aFolder
+   *         Parent folder whose children we will search
+   *  @param aSubFolder
+   *         Name of the folder to search for in folder
+   */
+  long long getChildFolder(in long long aFolder, in AString aSubFolder);
 
   /**
    * Inserts a bookmark separator into the given folder at the given index.
    * The separator can be removed using removeChildAt().
-   *  @param aParentId
-   *         The id of the parent folder
+   *  @param aFolder
+   *         Parent folder of the separator
    *  @param aIndex
    *         The separator's index under folder, or DEFAULT_INDEX to append
    *  @return The ID of the new separator.
    */
-  long long insertSeparator(in long long aParentId, in long aIndex);
+  long long insertSeparator(in long long aFolder, in long aIndex);
+
+  /**
+   * Removes any type of child (item, folder, or separator) at the given index.
+   *  @param aFolder
+   *         The folder to remove a child from
+   *  @param aIndex
+   *         The index of the child to remove
+   */
+  void removeChildAt(in long long aFolder, in long aIndex);
 
   /**
    * Get the itemId given the containing folder and the index.
-   *  @param aParentId
-   *         The id of the diret parent folder of the item
+   *  @param aFolder
+   *         The direct parent folder of the item
    *  @param aIndex
-   *         The index of the item within the parent folder.
-   *         Pass DEFAULT_INDEX for the last item.
+   *         The index of the item within aFolder, DEFAULT_INDEX for the last item
    *  @return The ID of the found item, -1 if the item does not exists.
    */
-  long long getIdForItemAt(in long long aParentId, in long aIndex);
+  long long getIdForItemAt(in long long aFolder, in long aIndex);
 
   /**
    * Get a globally unique identifier for an item, meant to be used in
    * sync scenarios.  Even if their contents are exactly the same
    * (including an item in a different profile with the same ItemId),
    * the GUID would be different.
    *  @param   aItemId
    *           The ID of the item to get the GUID for
@@ -468,20 +474,20 @@ interface nsINavBookmarksService : nsISu
   unsigned short getItemType(in long long aItemId);
 
   /**
    * Checks whether a folder is marked as read-only.
    * If this is set to true, UI will not allow the user to add, remove,
    * or reorder children in this folder. The default for all folders is false.
    * Note: This does not restrict API calls, only UI actions.
    *
-   * @param aItemId
+   * @param aFolder
    *        the item-id of the folder.
    */
-  boolean getFolderReadonly(in long long aItemId);
+  boolean getFolderReadonly(in long long aFolder);
 
   /**
    * Sets or unsets the readonly flag from a folder.
    * If this is set to true, UI will not allow the user to add, remove,
    * or reorder children in this folder. The default for all folders is false.
    * Note: This does not restrict API calls, only UI actions.
    *
    * @param aFolder
--- a/toolkit/components/places/public/nsINavHistoryService.idl
+++ b/toolkit/components/places/public/nsINavHistoryService.idl
@@ -16,19 +16,16 @@
  *
  * The Initial Developer of the Original Code is
  * Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Brett Wilson <brett@gmail.com>
- *   Dietrich Ayala <dietrich@mozilla.com>
- *   Marco Bonardo <mak77@bonardo.net>
- *   Asaf Romano <mano@mozilla.com>
  *
  * 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
@@ -53,18 +50,19 @@
 interface nsIFile;
 interface nsINavHistoryContainerResultNode;
 interface nsINavHistoryQueryResultNode;
 interface nsINavHistoryQuery;
 interface nsINavHistoryQueryOptions;
 interface nsINavHistoryResult;
 interface nsINavHistoryBatchCallback;
 interface nsITreeColumn;
+interface nsIWritablePropertyBag;
 
-[scriptable, uuid(464ae28f-3a9c-4483-afb2-bb0fb0ddb893)]
+[scriptable, uuid(47cf89e3-4777-46bf-9677-21793870ce62)]
 interface nsINavHistoryResultNode : nsISupports
 {
   /**
    * Indentifies the parent result node in the result set. This is null for
    * top level nodes.
    */
   readonly attribute nsINavHistoryContainerResultNode parent;
 
@@ -130,27 +128,46 @@ interface nsINavHistoryResultNode : nsIS
    * for the page. It is *not* the URI of the favicon, but rather something
    * that will resolve to the actual image.
    *
    * In most cases, this is an annotation URI that will query the favicon
    * service. If the entry has no favicon, this is the chrome URI of the
    * default favicon. If the favicon originally lived in chrome, this will
    * be the original chrome URI of the icon.
    */
-  readonly attribute AUTF8String icon;
+  readonly attribute nsIURI icon;
 
   /**
    * This is the number of levels between this node and the top of the
    * hierarchy. The members of result.children have indentLevel = 0, their
    * children have indentLevel = 1, etc. The indent level of the root node is
    * set to -1.
    */
   readonly attribute long indentLevel;
 
   /**
+   * Value with undefined meaning for use by the view. Its initial value will
+   * be -1. The result implementation treats nodes with this property set to
+   * -1 as invisible!
+   *
+   * View-implementations may use this value to track the node index in the
+   * view, e.g. the tree view uses this value to indicate the row in the
+   * tree that this node is at. Other views may choose not to use this, but
+   * should inititalize this value to anything but -1 for visible nodes.
+   */
+  attribute long viewIndex;
+
+  /**
+   * You can use this to associate temporary information with the result node.
+   * This property bag is associated with the result node and is not persisted
+   * in any way.
+   */
+  readonly attribute nsIWritablePropertyBag propertyBag;
+
+  /**
    * When this item is in a bookmark folder (parent is of type folder), this is
    * the index into that folder of this node. These indices start at 0 and
    * increase in the order that they appear in the bookmark folder. For items
    * that are not in a bookmark folder, this value is -1.
    */
   readonly attribute long bookmarkIndex;
 
   /**
@@ -419,191 +436,86 @@ interface nsINavHistoryQueryResultNode :
 [scriptable, uuid(e60f4429-3787-45c8-a8c0-18ef52621bbf)]
 interface nsINavHistoryResultViewer : nsISupports
 {
   /**
    * Called when 'aItem' is inserted into 'aParent' at index 'aNewIndex'.
    * The item previously at index (if any) and everything below it will have
    * been shifted down by one. The item may be a container or a leaf.
    */
-  void nodeInserted(in nsINavHistoryContainerResultNode aParent,
-                    in nsINavHistoryResultNode aNode,
+  void itemInserted(in nsINavHistoryContainerResultNode aParent,
+                    in nsINavHistoryResultNode aItem,
                     in unsigned long aNewIndex);
 
   /**
    * Called whan 'aItem' is removed from 'aParent' at 'aOldIndex'. The item
    * may be a container or a leaf. This function will be called after the item
    * has been removed from its parent list, but before anything else (including
    * NULLing out the item's parent) has happened.
    */
-  void nodeRemoved(in nsINavHistoryContainerResultNode aParent,
+  void itemRemoved(in nsINavHistoryContainerResultNode aParent,
                    in nsINavHistoryResultNode aItem,
                    in unsigned long aOldIndex);
 
   /**
    * Called whan 'aItem' is moved from 'aOldParent' at 'aOldIndex' to
    * aNewParent at aNewIndex. The item may be a container or a leaf.
    *
    * XXX: at the moment, this method is called only when an item is moved
    * within the same container. When an item is moved between containers,
    * a new node is created for the item, and the itemRemoved/itemAdded methods
    * are used.
    */
-  void nodeMoved(in nsINavHistoryResultNode aNode,
+  void itemMoved(in nsINavHistoryResultNode aItem,
                  in nsINavHistoryContainerResultNode aOldParent,
                  in unsigned long aOldIndex,
                  in nsINavHistoryContainerResultNode aNewParent,
                  in unsigned long aNewIndex);
 
   /**
-   * Called right after aNode's title has changed.
-   * 
-   * @param aNode
-   *        a result node
-   * @param aNewTitle
-   *        the new title
-   */
-  void nodeTitleChanged(in nsINavHistoryResultNode aNode,
-                        in AUTF8String aNewTitle);
-
-  /**
-   * Called right after aNode's uri property has changed.
-   * 
-   * @param aNode
-   *        a result node
-   * @param aNewURI
-   *        the new uri
-   */
-  void nodeURIChanged(in nsINavHistoryResultNode aNode,
-                      in AUTF8String aNewURI);
-
-  /**
-   * Called right after aNode's icon property has changed.
-   *
-   * @param aNode
-   *        a result node
-   *
-   * @note: The new icon is accessible through aNode.icon.
-   */
-  void nodeIconChanged(in nsINavHistoryResultNode aNode);
-
-  /**
-   * Called right after aNode's time property or accessCount property, or both,
-   * have changed.
-   *
-   * @param aNode
-   *        a uri result node
-   * @param aNewVisitDate
-   *        the new visit date
-   * @param aNewAccessCount
-   *        the new access-count
+   * Called when an item has been changed and should be repainted. This only
+   * refers to the specific item. If it is a container, getting this message
+   * does not imply anything happened to the children. You'll get separate
+   * messages for those. Also, this may be called for container nodes at times
+   * when the result thinks it's possible that a twisty mey need to bw redrawn.
    */
-  void nodeHistoryDetailsChanged(in nsINavHistoryResultNode aNode,
-                                 in PRTime aNewVisitDate,
-                                 in unsigned long aNewAccessCount);
-
-  /**
-   * Called when the tags set on the uri represented by aNode have changed.
-   *
-   * @param aNode
-   *        a uri result node
-   *
-   * @note: The new tags list is accessible through aNode.tags.
-   */
-  void nodeTagsChanged(in nsINavHistoryResultNode aNode);
-
-  /**
-   * Called right after the aNode's keyword property has changed.
-   * 
-   * @param aNode
-   *        a uri result node
-   * @param aNewKeyword
-   *        the new keyword
-   */
-  void nodeKeywordChanged(in nsINavHistoryResultNode aNode,
-                          in AUTF8String aNewKeyword);
-
-  /**
-   * Called right after an annotation of aNode's has changed (set, altered, or
-   * unset).
-   * 
-   * @param aNode
-   *        a result node
-   * @param aAnnoName
-   *        the name of the annotation that changed
-   */
-  void nodeAnnotationChanged(in nsINavHistoryResultNode aNode,
-                             in AUTF8String aAnnoName);
-
-  /**
-   * Called right after aNode's dateAdded property has changed.
-   *
-   * @param aNode
-   *        a result node
-   * @param aNewValue
-   *        the new value of the dateAdded property
-   */
-  void nodeDateAddedChanged(in nsINavHistoryResultNode aNode,
-                            in PRTime aNewValue);
-
-  /**
-   * Called right after aNode's dateModified property has changed.
-   *
-   * @param aNode
-   *        a result node
-   * @param aNewValue
-   *        the new value of the dateModified property
-   */
-  void nodeLastModifiedChanged(in nsINavHistoryResultNode aNode,
-                               in PRTime aNewValue);
+  void itemChanged(in nsINavHistoryResultNode item);
 
   /**
    * Called when an item is being replaced with another item at the exact
    * same position.
-   *
-   * @param aParentNode
-   *        the parent node of the node which is being replaced
-   * @param aOldNode
-   *        the node which is being replaced
-   * @param aNewNode
-   *        the new node
-   * @param aParentNode
-   *        the index in aParentNode, at which a node is being replaced
    */
-  void nodeReplaced(in nsINavHistoryContainerResultNode aParentNode,
-                    in nsINavHistoryResultNode aOldNode,
-                    in nsINavHistoryResultNode aNewNode,
-                    in unsigned long aIndex);
+  void itemReplaced(in nsINavHistoryContainerResultNode parent,
+                    in nsINavHistoryResultNode oldItem,
+                    in nsINavHistoryResultNode newItem,
+                    in unsigned long index);
 
   /**
    * Called after a container node went from closed to opened.
-   *
-   * @param aContainerNode
-   *        the container node which was opened
    */
-  void containerOpened(in nsINavHistoryContainerResultNode aContainerNode);
+  void containerOpened(in nsINavHistoryContainerResultNode item);
 
   /**
    * Called after a container node went from opened to closed. This will be
    * called for the topmost container that is closing, and implies that any
    * child containers have closed as well.
-   *
-   * @param aContainerNode
-   *        the container node which was closed
    */
-  void containerClosed(in nsINavHistoryContainerResultNode aContainerNode);
+  void containerClosed(in nsINavHistoryContainerResultNode item);
 
   /**
    * Called when something significant has happened within the container. The
    * contents of the container should be re-built.
-   *
-   * @param aContainerNode
-   *        the container node to invalidate
    */
-  void invalidateContainer(in nsINavHistoryContainerResultNode aContainerNode);
+  void invalidateContainer(in nsINavHistoryContainerResultNode item);
+
+  /**
+   * Called when something significant is changing that requires everything
+   * to be recomputed. For example, changing sorting can affect every row.
+   */
+  void invalidateAll();
 
   /**
    * This is called to indicate to the UI that the sort has changed to the
    * given mode. For trees, for example, this would update the column headers
    * to reflect the sorting. For many other types of views, this won't be
    * applicable.
    *
    * @param sortingMode  One of nsINavHistoryQueryOptions.SORT_BY_* that
@@ -619,18 +531,16 @@ interface nsINavHistoryResultViewer : ns
    * nsINavHistoryResult.viewer. This will be set to NULL when the result
    * is being deallocated. This should not be set by other code.
    */
   attribute nsINavHistoryResult result;
 };
 
 
 /**
- * TODO: Bug 517719.
- *
  * A predefined view adaptor for interfacing results with an nsITree. This
  * object will remove itself from its associated result when the tree has been
  * detached. This prevents circular references. Users should be aware of this,
  * if you want to re-use the same viewer, you will need to keep your own
  * reference to it and re-initialize it when the tree changes. If you use this
  * object, attach it to a result, never attach it to a tree, and forget about
  * it, it will leak!
  */
@@ -989,17 +899,17 @@ interface nsINavHistoryQuery : nsISuppor
    * Creates a new query item with the same parameters of this one.
    */
   nsINavHistoryQuery clone();
 };
 
 /**
  * This object represents the global options for executing a query.
  */
-[scriptable, uuid(c6831388-fd4c-46a8-85f3-952917b66d72)]
+[scriptable, uuid(b3d5de06-f8ef-4433-84c2-b8b237403b2a)]
 interface nsINavHistoryQueryOptions : nsISupports
 {
   /**
    * You can ask for the results to be pre-sorted. Since the DB has indices
    * of many items, it can produce sorted results almost for free. These should
    * be self-explanatory.
    *
    * Note: re-sorting is slower, as is sorting by title or when you have a
@@ -1190,16 +1100,22 @@ interface nsINavHistoryQueryOptions : ns
    * Defines how redirects should be handled, see REDIRECTS_MODE_* constants
    * above.
    * Defaults to REDIRECTS_MODE_ALL.
    * Note: this option is effective only on QUERY_TYPE_HISTORY.
    */
   attribute unsigned short redirectsMode;
 
   /**
+   * Separate/group history items based on session information.  Only
+   * matters when sorting by date.
+   */
+  attribute boolean showSessions;
+
+  /**
    * This is the maximum number of results that you want. The query is exeucted,
    * the results are sorted, and then the top 'maxResults' results are taken
    * and returned. Set to 0 (the default) to get all results.
    *
    * THIS DOES NOT WORK IN CONJUNCTION WITH SORTING BY TITLE. This is because
    * sorting by title requires us to sort after using locale-sensetive sorting
    * (as opposed to letting the database do it for us).
    *
--- a/toolkit/components/places/src/nsLivemarkService.js
+++ b/toolkit/components/places/src/nsLivemarkService.js
@@ -473,17 +473,17 @@ LivemarkService.prototype = {
   onBeginUpdateBatch: function() { },
   onEndUpdateBatch: function() { },
   onItemAdded: function() { },
   onItemChanged: function() { },
   onItemVisited: function() { },
   onItemMoved: function() { },
   onBeforeItemRemoved: function() { },
 
-  onItemRemoved: function(aItemId, aParentId, aIndex, aItemType) {
+  onItemRemoved: function(aItemId, aParentId, aIndex) {
     // we don't need to remove annotations since itemAnnotations
     // are already removed with the bookmark
     try {
       var livemarkIndex = this._getLivemarkIndex(aItemId);
     }
     catch(ex) {
       // not a livemark
       return;
--- a/toolkit/components/places/src/nsNavBookmarks.cpp
+++ b/toolkit/components/places/src/nsNavBookmarks.cpp
@@ -1181,17 +1181,17 @@ nsNavBookmarks::InsertBookmark(PRInt64 a
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AddBookmarkToHash(childID, 0);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK))
+                      OnItemAdded(*aNewBookmarkId, aFolder, index))
 
   // If the bookmark has been added to a tag container, notify all
   // bookmark-folder result nodes which contain a bookmark for the new
   // bookmark's url
   PRInt64 grandParentId;
   rv = GetFolderIdForItem(aFolder, &grandParentId);
   NS_ENSURE_SUCCESS(rv, rv);
   if (grandParentId == mTagRoot) {
@@ -1199,22 +1199,18 @@ nsNavBookmarks::InsertBookmark(PRInt64 a
     nsTArray<PRInt64> bookmarks;
 
     rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (bookmarks.Length()) {
       for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
         ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                            OnItemChanged(bookmarks[i],
-                                          NS_LITERAL_CSTRING("tags"),
-                                          PR_FALSE,
-                                          EmptyCString(),
-                                          0,
-                                          TYPE_BOOKMARK))
+                            OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
+                                          PR_FALSE, EmptyCString()))
       }
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::RemoveItem(PRInt64 aItemId)
@@ -1251,17 +1247,17 @@ nsNavBookmarks::RemoveItem(PRInt64 aItem
 
   if (itemType == TYPE_FOLDER) {
     rv = RemoveFolder(aItemId);
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnBeforeItemRemoved(aItemId, itemType))
+                      OnBeforeItemRemoved(aItemId))
 
   mozStorageTransaction transaction(mDBConn, PR_FALSE);
 
   // First, remove item annotations
   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
   rv = annosvc->RemoveItemAnnotations(aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1290,17 +1286,17 @@ nsNavBookmarks::RemoveItem(PRInt64 aItem
     // UpdateFrecency needs to know whether placeId is still bookmarked.
     // Although we removed aItemId, placeId may still be bookmarked elsewhere;
     // IsRealBookmark will know.
     rv = History()->UpdateFrecency(placeId, IsRealBookmark(placeId));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemRemoved(aItemId, folderId, childIndex, itemType))
+                      OnItemRemoved(aItemId, folderId, childIndex))
 
   if (itemType == TYPE_BOOKMARK) {
     // If the removed bookmark was a child of a tag container, notify all
     // bookmark-folder result nodes which contain a bookmark for the removed
     // bookmark's url.
     PRInt64 grandParentId;
     rv = GetFolderIdForItem(folderId, &grandParentId);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1311,22 +1307,18 @@ nsNavBookmarks::RemoveItem(PRInt64 aItem
       nsTArray<PRInt64> bookmarks;
 
       rv = GetBookmarkIdsForURITArray(uri, &bookmarks);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (bookmarks.Length()) {
         for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
           ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                              OnItemChanged(bookmarks[i],
-                                            NS_LITERAL_CSTRING("tags"),
-                                            PR_FALSE,
-                                            EmptyCString(),
-                                            0,
-                                            TYPE_BOOKMARK))
+                              OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
+                                            PR_FALSE, EmptyCString()))
         }
       }
     }
   }
   return NS_OK;
 }
 
 
@@ -1425,17 +1417,17 @@ nsNavBookmarks::CreateContainerWithID(PR
   rv = InsertBookmarkInDB(aItemId, nsnull, containerType, aParent, index,
                           aName, PR_Now(), nsnull, aContractId, aNewFolder);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemAdded(*aNewFolder, aParent, index, containerType))
+                      OnItemAdded(*aNewFolder, aParent, index))
 
   *aIndex = index;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex,
                                 PRInt64* aNewItemId)
@@ -1469,17 +1461,17 @@ nsNavBookmarks::InsertSeparator(PRInt64 
                           voidString, PR_Now(), nsnull, EmptyString(),
                           aNewItemId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR))
+                      OnItemAdded(*aNewItemId, aParent, index))
 
   return NS_OK;
 }
 
 nsresult
 nsNavBookmarks::GetLastChildId(PRInt64 aFolder, PRInt64* aItemId)
 {
   nsCOMPtr<mozIStorageStatement> statement;
@@ -1534,16 +1526,62 @@ nsNavBookmarks::GetIdForItemAt(PRInt64 a
       }
       else
         *aItemId = mDBGetChildAt->AsInt64(0);
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNavBookmarks::RemoveChildAt(PRInt64 aParent, PRInt32 aIndex)
+{
+  NS_ENSURE_ARG_MIN(aParent, 1);
+
+  mozStorageTransaction transaction(mDBConn, PR_FALSE);
+  nsresult rv;
+  PRInt64 id;
+  PRInt32 type;
+
+  {
+    mozStorageStatementScoper scope(mDBGetChildAt);
+    rv = mDBGetChildAt->BindInt64Parameter(0, aParent);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRBool hasMore;
+    rv = mDBGetChildAt->ExecuteStep(&hasMore);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!hasMore) {
+      // Child doesn't exist
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    id = mDBGetChildAt->AsInt64(0);
+    type = mDBGetChildAt->AsInt32(2);
+  }
+
+  if (type == TYPE_BOOKMARK || type == TYPE_SEPARATOR) {
+    // Commit this transaction so that we don't notify observers mid-tranaction
+    rv = transaction.Commit();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return RemoveItem(id);
+  }
+  else if (type == TYPE_FOLDER) {
+    // Commit this transaction so that we don't notify observers mid-tranaction
+    rv = transaction.Commit();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return RemoveFolder(id);
+  }
+  return NS_ERROR_INVALID_ARG;
+}
+
 nsresult 
 nsNavBookmarks::GetParentAndIndexOfFolder(PRInt64 aFolder, PRInt64* aParent, 
                                           PRInt32* aIndex)
 {
   nsCAutoString buffer;
   buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE id = ");
   buffer.AppendInt(aFolder);
 
@@ -1559,23 +1597,23 @@ nsNavBookmarks::GetParentAndIndexOfFolde
   }
 
   *aParent = statement->AsInt64(0);
   *aIndex = statement->AsInt32(1);
   
   return NS_OK;
 }
 
-NS_HIDDEN_(nsresult)
+NS_IMETHODIMP
 nsNavBookmarks::RemoveFolder(PRInt64 aFolderId)
 {
   NS_ENSURE_TRUE(aFolderId != mRoot, NS_ERROR_INVALID_ARG);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnBeforeItemRemoved(aFolderId, TYPE_FOLDER))
+                      OnBeforeItemRemoved(aFolderId))
 
   mozStorageTransaction transaction(mDBConn, PR_FALSE);
 
   nsresult rv;
   PRInt64 parent;
   PRInt32 index, type;
   nsCAutoString folderType;
   {
@@ -1639,17 +1677,17 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFo
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aFolderId == mToolbarFolder) {
     mToolbarFolder = 0;
   }
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemRemoved(aFolderId, parent, index, TYPE_FOLDER))
+                      OnItemRemoved(aFolderId, parent, index))
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction, nsITransaction)
 
 NS_IMETHODIMP
 nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolderId, nsITransaction** aResult)
@@ -1760,17 +1798,17 @@ nsNavBookmarks::RemoveFolderChildren(PRI
 
   // Build a string of folders whose children will be removed.
   nsCString foldersToRemove;
   for (PRUint32 i = 0; i < folderChildrenArray.Length(); i++) {
     folderChildrenInfo child = folderChildrenArray[i];
 
     // Notify observers that we are about to remove this child.
     ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                        OnBeforeItemRemoved(child.itemId, child.itemType))
+                        OnBeforeItemRemoved(child.itemId))
 
     if (child.itemType == TYPE_FOLDER) {
       foldersToRemove.AppendLiteral(",");
       foldersToRemove.AppendInt(child.itemId);
 
       // If this is a dynamic container, try to notify its service that we
       // are going to remove it.
       // XXX (bug 484094) this should use a bookmark observer!
@@ -1835,18 +1873,17 @@ nsNavBookmarks::RemoveFolderChildren(PRI
 
   // Call observers in reverse order to serve children before their parent.
   for (PRInt32 i = folderChildrenArray.Length() - 1; i >= 0 ; i--) {
     folderChildrenInfo child = folderChildrenArray[i];
 
     ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
                         OnItemRemoved(child.itemId,
                                       child.parentId,
-                                      child.index,
-                                      child.itemType));
+                                      child.index));
 
     if (child.itemType == TYPE_BOOKMARK) {
       // If the removed bookmark was a child of a tag container, notify all
       // bookmark-folder result nodes which contain a bookmark for the removed
       // bookmark's url.
 
       if (child.grandParentId == mTagRoot) {
         nsCOMPtr<nsIURI> uri;
@@ -1855,22 +1892,18 @@ nsNavBookmarks::RemoveFolderChildren(PRI
 
         nsTArray<PRInt64> bookmarks;
         rv = GetBookmarkIdsForURITArray(uri, &bookmarks);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (bookmarks.Length()) {
           for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
             ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                                OnItemChanged(bookmarks[i],
-                                              NS_LITERAL_CSTRING("tags"),
-                                              PR_FALSE,
-                                              EmptyCString(),
-                                              0,
-                                              TYPE_BOOKMARK))
+                                OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
+                                              PR_FALSE, EmptyCString()))
           }
         }
       }
     }
   }
 
   return NS_OK;
 }
@@ -2021,30 +2054,65 @@ nsNavBookmarks::MoveItem(PRInt64 aItemId
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // notify bookmark observers
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
                       OnItemMoved(aItemId, oldParent, oldIndex, aNewParent,
-                                  newIndex, itemType))
+                                  newIndex))
 
   // notify dynamic container provider if there is one
   if (!folderType.IsEmpty()) {
     nsCOMPtr<nsIDynamicContainer> container =
       do_GetService(folderType.get(), &rv);
     if (NS_SUCCEEDED(rv)) {
       rv = container->OnContainerMoved(aItemId, aNewParent, newIndex);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNavBookmarks::GetChildFolder(PRInt64 aFolder, const nsAString& aSubFolder,
+                               PRInt64* _result)
+{
+  // note: we allow empty folder names
+  nsresult rv;
+  if (aFolder == 0)
+    return NS_ERROR_INVALID_ARG;
+
+  // If this gets used a lot, we'll want a precompiled statement
+  nsCAutoString getChildFolderQuery =
+    NS_LITERAL_CSTRING("SELECT id "
+                       "FROM moz_bookmarks "
+                       "WHERE parent = ?1 AND type = ") +
+    nsPrintfCString("%d", TYPE_FOLDER) +
+    NS_LITERAL_CSTRING(" AND title = ?2");
+  nsCOMPtr<mozIStorageStatement> statement;
+  rv = mDBConn->CreateStatement(getChildFolderQuery, getter_AddRefs(statement));
+  NS_ENSURE_SUCCESS(rv, rv);
+  statement->BindInt64Parameter(0, aFolder);
+  statement->BindStringParameter(1, aSubFolder);
+
+  PRBool hasResult = PR_FALSE;
+  rv = statement->ExecuteStep(&hasResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (! hasResult) {
+    // item not found
+    *_result = 0;
+    return NS_OK;
+  }
+
+  return statement->GetInt64(0, _result);
+}
+
 nsresult
 nsNavBookmarks::SetItemDateInternal(mozIStorageStatement* aStatement, PRInt64 aItemId, PRTime aValue)
 {
   mozStorageStatementScoper scope(aStatement);
   nsresult rv = aStatement->BindInt64Parameter(0, aValue);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStatement->BindInt64Parameter(1, aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2056,31 +2124,24 @@ nsNavBookmarks::SetItemDateInternal(mozI
   // that the item has changed.
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId, PRTime aDateAdded)
 {
-  // GetItemType also ensures that aItemId points to a valid item.
-  PRUint16 itemType;
-  nsresult rv = GetItemType(aItemId, &itemType);
+  NS_ENSURE_ARG_MIN(aItemId, 1);
+
+  nsresult rv = SetItemDateInternal(mDBSetItemDateAdded, aItemId, aDateAdded);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = SetItemDateInternal(mDBSetItemDateAdded, aItemId, aDateAdded);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded.
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
                       OnItemChanged(aItemId, NS_LITERAL_CSTRING("dateAdded"),
-                                    PR_FALSE,
-                                    nsPrintfCString(16, "%lld", aDateAdded),
-                                    aDateAdded,
-                                    itemType));
+                                    PR_FALSE, nsPrintfCString(16, "%lld", aDateAdded)));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::GetItemDateAdded(PRInt64 aItemId, PRTime *aDateAdded)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
   NS_ENSURE_ARG_POINTER(aDateAdded);
@@ -2097,31 +2158,24 @@ nsNavBookmarks::GetItemDateAdded(PRInt64
     return NS_ERROR_INVALID_ARG; // invalid item id
 
   return mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_DateAdded, aDateAdded);
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::SetItemLastModified(PRInt64 aItemId, PRTime aLastModified)
 {
-  // GetItemType also ensures that aItemId points to a valid item.
-  PRUint16 itemType;
-  nsresult rv = GetItemType(aItemId, &itemType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, aLastModified);
+  NS_ENSURE_ARG_MIN(aItemId, 1);
+
+  nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, aLastModified);
   NS_ENSURE_SUCCESS(rv, rv);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemChanged(aItemId,
-                                    NS_LITERAL_CSTRING("lastModified"),
-                                    PR_FALSE,
-                                    nsPrintfCString(16, "%lld", aLastModified),
-                                    aLastModified,
-                                    itemType));
+                      OnItemChanged(aItemId, NS_LITERAL_CSTRING("lastModified"),
+                                    PR_FALSE, nsPrintfCString(16, "%lld", aLastModified)));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::GetItemLastModified(PRInt64 aItemId, PRTime *aLastModified)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
   NS_ENSURE_ARG_POINTER(aLastModified);
@@ -2220,23 +2274,20 @@ nsNavBookmarks::GetItemIdForGUID(const n
 
   // found, get the itemId
   return mDBGetItemIdForGUID->GetInt64(0, aItemId);
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::SetItemTitle(PRInt64 aItemId, const nsACString &aTitle)
 {
-  // GetItemType also ensures that aItemId points to a valid item.
-  PRUint16 itemType;
-  nsresult rv = GetItemType(aItemId, &itemType);
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_ARG_MIN(aItemId, 1);
 
   nsCOMPtr<mozIStorageStatement> statement;
-  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "UPDATE moz_bookmarks SET title = ?1, lastModified = ?2 WHERE id = ?3"),
     getter_AddRefs(statement));
   NS_ENSURE_SUCCESS(rv, rv);
   // Support setting a null title, we support this in insertBookmark.
   if (aTitle.IsVoid())
     rv = statement->BindNullParameter(0);
   else
     rv = statement->BindUTF8StringParameter(0, aTitle);
@@ -2246,22 +2297,18 @@ nsNavBookmarks::SetItemTitle(PRInt64 aIt
   NS_ENSURE_SUCCESS(rv, rv);
   rv = statement->BindInt64Parameter(2, aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemChanged(aItemId,
-                                    NS_LITERAL_CSTRING("title"),
-                                    PR_FALSE,
-                                    aTitle,
-                                    lastModified,
-                                    itemType));
+                      OnItemChanged(aItemId, NS_LITERAL_CSTRING("title"),
+                                    PR_FALSE, aTitle));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::GetItemTitle(PRInt64 aItemId, nsACString &aTitle)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
@@ -2647,18 +2694,17 @@ nsNavBookmarks::ChangeBookmarkURI(PRInt6
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCAutoString spec;
   rv = aNewURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Pass the new URI to OnItemChanged.
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-    OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("uri"), PR_FALSE, spec,
-                  lastModified, TYPE_BOOKMARK))
+    OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("uri"), PR_FALSE, spec))
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::GetFolderIdForItem(PRInt64 aItemId, PRInt64 *aFolderId)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
@@ -2762,31 +2808,29 @@ NS_IMETHODIMP
 nsNavBookmarks::SetItemIndex(PRInt64 aItemId, PRInt32 aNewIndex)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
   NS_ENSURE_ARG_MIN(aNewIndex, 0);
 
   nsresult rv;
   PRInt32 oldIndex = 0;
   PRInt64 parent = 0;
-  PRUint16 itemType;
 
   {
     mozStorageStatementScoper scopeGet(mDBGetItemProperties);
     rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRBool results;
     rv = mDBGetItemProperties->ExecuteStep(&results);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!results)
       return NS_OK;
 
     oldIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
-    itemType = (PRUint16)mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
     parent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
   }
 
   // Ensure we are not going out of range.
   PRInt32 folderCount;
   rv = FolderCount(parent, &folderCount);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG);
@@ -2795,19 +2839,24 @@ nsNavBookmarks::SetItemIndex(PRInt64 aIt
   rv = mDBSetItemIndex->BindInt64Parameter(0, aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mDBSetItemIndex->BindInt32Parameter(1, aNewIndex);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mDBSetItemIndex->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // XXX (bug 484096) this is really inefficient and we should look into using
+  //     onItemChanged here!
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                      OnItemMoved(aItemId, parent, oldIndex, parent,
-                                  aNewIndex, itemType))
+                      OnBeforeItemRemoved(aItemId))
+  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
+                      OnItemRemoved(aItemId, parent, oldIndex))
+  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
+                      OnItemAdded(aItemId, parent, aNewIndex))
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId, const nsAString& aKeyword)
 {
   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
@@ -2885,18 +2934,17 @@ nsNavBookmarks::SetKeywordForBookmark(PR
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Pass the new keyword to OnItemChanged.
   ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
                       OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("keyword"),
-                                    PR_FALSE, NS_ConvertUTF16toUTF8(aKeyword),
-                                    lastModified, TYPE_BOOKMARK))
+                                    PR_FALSE, NS_ConvertUTF16toUTF8(aKeyword)))
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword)
 {
   NS_ENSURE_ARG(aURI);
@@ -3089,22 +3137,18 @@ nsNavBookmarks::OnDeleteURI(nsIURI *aURI
     nsTArray<PRInt64> bookmarks;
 
     nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (bookmarks.Length()) {
       for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
         ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                            OnItemChanged(bookmarks[i],
-                                          NS_LITERAL_CSTRING("cleartime"),
-                                          PR_FALSE,
-                                          EmptyCString(),
-                                          0,
-                                          TYPE_BOOKMARK))
+                            OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("cleartime"),
+                                          PR_FALSE, EmptyCString()))
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::OnClearHistory()
 {
@@ -3142,38 +3186,30 @@ nsNavBookmarks::OnPageChanged(nsIURI *aU
       nsCOMPtr<nsNavHistoryQueryOptions> options;
       rv = history->QueryStringToQueryArray(spec, &queries, getter_AddRefs(options));
       NS_ENSURE_SUCCESS(rv, rv);
 
       NS_ENSURE_STATE(queries.Count() == 1);
       NS_ENSURE_STATE(queries[0]->Folders().Length() == 1);
 
       ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                          OnItemChanged(queries[0]->Folders()[0],
-                                        NS_LITERAL_CSTRING("favicon"),
-                                        PR_FALSE,
-                                        NS_ConvertUTF16toUTF8(aValue),
-                                        0,
-                                        TYPE_BOOKMARK));
+                          OnItemChanged(queries[0]->Folders()[0], NS_LITERAL_CSTRING("favicon"),
+                                        PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
     }
     else {
       // query for all bookmarks for that URI, notify for each 
       nsTArray<PRInt64> bookmarks;
       rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
       NS_ENSURE_SUCCESS(rv, rv);
 
       if (bookmarks.Length()) {
         for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
           ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
-                              OnItemChanged(bookmarks[i],
-                                            NS_LITERAL_CSTRING("favicon"),
-                                            PR_FALSE,
-                                            NS_ConvertUTF16toUTF8(aValue),
-                                            0,
-                                            TYPE_BOOKMARK));
+                              OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("favicon"),
+                                            PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
       }
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::OnPageExpired(nsIURI* aURI, PRTime aVisitTime,
@@ -3189,56 +3225,40 @@ NS_IMETHODIMP
 nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId, const nsACString& aName)
 {
-  // GetItemType also ensures that aItemId points to a valid item.
-  PRUint16 itemType;
-  nsresult rv = GetItemType(aItemId, &itemType);
+  nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRTime lastModified = PR_Now();
-  rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, lastModified);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                      nsINavBookmarkObserver,
-                      OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString(),
-                                    lastModified, itemType));
+  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
+                      OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId, const nsACString& aName)
 {
-  // GetItemType also ensures that aItemId points to a valid item.
-  PRUint16 itemType;
-  nsresult rv = GetItemType(aItemId, &itemType);
+  nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRTime lastModified = PR_Now();
-  rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, lastModified);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                      nsINavBookmarkObserver,
-                      OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString(),
-                                    lastModified, itemType));
+  ENUMERATE_OBSERVERS(mCanNotify, mCacheObservers, mObservers, nsINavBookmarkObserver,
+                      OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
 
   return NS_OK;
 }
 
 PRBool
 nsNavBookmarks::ItemExists(PRInt64 aItemId) {
   mozStorageStatementScoper scope(mDBGetItemProperties);
   nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
--- a/toolkit/components/places/src/nsNavBookmarks.h
+++ b/toolkit/components/places/src/nsNavBookmarks.h
@@ -130,18 +130,16 @@ private:
   nsresult CreateRoot(mozIStorageStatement* aGetRootStatement,
                       const nsCString& name, PRInt64* aID,
                       PRInt64 aParentID, PRBool* aWasCreated);
 
   nsresult AdjustIndices(PRInt64 aFolder,
                          PRInt32 aStartIndex, PRInt32 aEndIndex,
                          PRInt32 aDelta);
 
-  NS_HIDDEN_(nsresult) RemoveFolder(PRInt64 aFolderId);
-
   /**
    * Calculates number of children for the given folder.
    *
    * @param aFolderId Folder to count children for.
    *
    * @return aFolderCount The number of children in this folder.
    *
    * @throws If folder does not exist.
--- a/toolkit/components/places/src/nsNavHistoryQuery.cpp
+++ b/toolkit/components/places/src/nsNavHistoryQuery.cpp
@@ -165,16 +165,17 @@ static void SetOptionsKeyUint32(const ns
 #define QUERYKEY_EXCLUDE_ITEMS "excludeItems"
 #define QUERYKEY_EXCLUDE_QUERIES "excludeQueries"
 #define QUERYKEY_EXCLUDE_READ_ONLY_FOLDERS "excludeReadOnlyFolders"
 #define QUERYKEY_EXCLUDE_ITEM_IF_PARENT_HAS_ANNOTATION "excludeItemIfParentHasAnnotation"
 #define QUERYKEY_EXPAND_QUERIES "expandQueries"
 #define QUERYKEY_FORCE_ORIGINAL_TITLE "originalTitle"
 #define QUERYKEY_INCLUDE_HIDDEN "includeHidden"
 #define QUERYKEY_REDIRECTS_MODE "redirectsMode"
+#define QUERYKEY_SHOW_SESSIONS "showSessions"
 #define QUERYKEY_MAX_RESULTS "maxResults"
 #define QUERYKEY_QUERY_TYPE "queryType"
 #define QUERYKEY_TAG "tag"
 #define QUERYKEY_NOTTAGS "!tags"
 
 inline void AppendAmpersandIfNonempty(nsACString& aString)
 {
   if (! aString.IsEmpty())
@@ -596,16 +597,22 @@ nsNavHistory::QueriesToQueryString(nsINa
 
   // redirects mode
   if (options->RedirectsMode() !=  nsINavHistoryQueryOptions::REDIRECTS_MODE_ALL) {
     AppendAmpersandIfNonempty(queryString);
     queryString += NS_LITERAL_CSTRING(QUERYKEY_REDIRECTS_MODE "=");
     AppendInt16(queryString, options->RedirectsMode());
   }
 
+  // show sessions
+  if (options->ShowSessions()) {
+    AppendAmpersandIfNonempty(queryString);
+    queryString += NS_LITERAL_CSTRING(QUERYKEY_SHOW_SESSIONS "=1");
+  }
+
   // max results
   if (options->MaxResults()) {
     AppendAmpersandIfNonempty(queryString);
     queryString += NS_LITERAL_CSTRING(QUERYKEY_MAX_RESULTS "=");
     AppendInt32(queryString, options->MaxResults());
   }
 
   // queryType
@@ -857,16 +864,20 @@ nsNavHistory::TokensToQueries(const nsTA
     // include hidden
     } else if (kvp.key.EqualsLiteral(QUERYKEY_INCLUDE_HIDDEN)) {
       SetOptionsKeyBool(kvp.value, aOptions,
                         &nsINavHistoryQueryOptions::SetIncludeHidden);
     // query type
     } else if (kvp.key.EqualsLiteral(QUERYKEY_REDIRECTS_MODE)) {
       SetOptionsKeyUint16(kvp.value, aOptions,
                           &nsINavHistoryQueryOptions::SetRedirectsMode);
+    // show sessions
+    } else if (kvp.key.EqualsLiteral(QUERYKEY_SHOW_SESSIONS)) {
+      SetOptionsKeyBool(kvp.value, aOptions,
+                        &nsINavHistoryQueryOptions::SetShowSessions);
     // max results
     } else if (kvp.key.EqualsLiteral(QUERYKEY_MAX_RESULTS)) {
       SetOptionsKeyUint32(kvp.value, aOptions,
                           &nsINavHistoryQueryOptions::SetMaxResults);
     // query type
     } else if (kvp.key.EqualsLiteral(QUERYKEY_QUERY_TYPE)) {
       SetOptionsKeyUint16(kvp.value, aOptions,
                           &nsINavHistoryQueryOptions::SetQueryType);
@@ -1482,16 +1493,30 @@ nsNavHistoryQueryOptions::GetRedirectsMo
 }
 NS_IMETHODIMP
 nsNavHistoryQueryOptions::SetRedirectsMode(PRUint16 aRedirectsMode)
 {
   mRedirectsMode = aRedirectsMode;
   return NS_OK;
 }
 
+// showSessions
+NS_IMETHODIMP
+nsNavHistoryQueryOptions::GetShowSessions(PRBool* aShowSessions)
+{
+  *aShowSessions = mShowSessions;
+  return NS_OK;
+}
+NS_IMETHODIMP
+nsNavHistoryQueryOptions::SetShowSessions(PRBool aShowSessions)
+{
+  mShowSessions = aShowSessions;
+  return NS_OK;
+}
+
 // maxResults
 NS_IMETHODIMP
 nsNavHistoryQueryOptions::GetMaxResults(PRUint32* aMaxResults)
 {
   *aMaxResults = mMaxResults;
   return NS_OK;
 }
 NS_IMETHODIMP
@@ -1537,16 +1562,17 @@ nsNavHistoryQueryOptions::Clone(nsNavHis
   if (! result)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsRefPtr<nsNavHistoryQueryOptions> resultHolder(result);
   result->mSort = mSort;
   result->mResultType = mResultType;
   result->mExcludeItems = mExcludeItems;
   result->mExcludeQueries = mExcludeQueries;
+  result->mShowSessions = mShowSessions;
   result->mExpandQueries = mExpandQueries;
   result->mMaxResults = mMaxResults;
   result->mQueryType = mQueryType;
   result->mParentAnnotationToExclude = mParentAnnotationToExclude;
 
   resultHolder.swap(*aResult);
   return NS_OK;
 }
--- a/toolkit/components/places/src/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/src/nsNavHistoryResult.cpp
@@ -50,21 +50,20 @@
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "prtime.h"
 #include "prprf.h"
 
 #include "nsIDynamicContainer.h"
+#include "nsHashPropertyBag.h"
+#include "nsIWritablePropertyBag.h"
 #include "mozStorageHelper.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIClassInfo.h"
-#include "nsIProgrammingLanguage.h"
-#include "nsIXPCScriptable.h"
 
 // What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors,
 // but some of our classes (like nsNavHistoryResult) have an ambiguous base
 // class of nsISupports which prevents this from working (the default macro
 // converts it to nsISupports, then addrefs it, then returns it). Therefore, we
 // expand the macro here and change it so that it works. Yuck.
 #define NS_INTERFACE_MAP_STATIC_AMBIGUOUS(_class) \
   if (aIID.Equals(NS_GET_IID(_class))) { \
@@ -82,140 +81,32 @@ inline PRInt32 ComparePRTime(PRTime a, P
     return 1;
   return 0;
 }
 inline PRInt32 CompareIntegers(PRUint32 a, PRUint32 b)
 {
   return a - b;
 }
 
-namespace mozilla {
-  namespace places {
-    // Class-info and the scriptable helper are implemented in order to
-    // allow the JS frontend code to set expando properties on result nodes.
-    class ResultNodeClassInfo : public nsIClassInfo
-                              , public nsIXPCScriptable
-    {
-      NS_DECL_ISUPPORTS
-      NS_DECL_NSIXPCSCRIPTABLE
-
-      // TODO: Bug 517718.
-      NS_IMETHODIMP
-      GetInterfaces(PRUint32 *_count, nsIID ***_array)
-      {
-        *_count = 0;
-        *_array = nsnull;
-
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetHelperForLanguage(PRUint32 aLanguage, nsISupports **_helper)
-      {
-        if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
-          *_helper = static_cast<nsIXPCScriptable *>(this);
-          NS_ADDREF(*_helper);
-        }
-        else
-          *_helper = nsnull;
-
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetContractID(char **_contractID)
-      {
-        *_contractID = nsnull;
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetClassDescription(char **_desc)
-      {
-        *_desc = nsnull;
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetClassID(nsCID **_id)
-      {
-        *_id = nsnull;
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetImplementationLanguage(PRUint32 *_language)
-      {
-        *_language = nsIProgrammingLanguage::CPLUSPLUS;
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetFlags(PRUint32 *_flags)
-      {
-        *_flags = 0;
-        return NS_OK;
-      }
-
-      NS_IMETHODIMP
-      GetClassIDNoAlloc(nsCID *_cid)
-      {
-        return NS_ERROR_NOT_AVAILABLE;
-      }
-    };
-
-    /**
-     * As a static implementation of classinfo, we violate XPCOM rules andjust
-     * pretend to use the refcount mechanism.  See classinfo documentation at
-     * https://developer.mozilla.org/en/Using_nsIClassInfo
-     */
-    NS_IMETHODIMP_(nsrefcnt) ResultNodeClassInfo::AddRef()
-    {
-      return 2;
-    }
-    NS_IMETHODIMP_(nsrefcnt) ResultNodeClassInfo::Release()
-    {
-      return 1;
-    }
-
-    NS_IMPL_QUERY_INTERFACE2(ResultNodeClassInfo, nsIClassInfo, nsIXPCScriptable)
-
-#define XPC_MAP_CLASSNAME ResultNodeClassInfo
-#define XPC_MAP_QUOTED_CLASSNAME "ResultNodeClassInfo"
-#define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \
-                      nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \
-                      nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
-
-// xpc_map_end contains implementation for nsIXPCScriptable, that used the
-// constant define above
-#include "xpc_map_end.h"    
-
-    static ResultNodeClassInfo sResultNodeClassInfo;
-  } // namespace places
-} // namespace mozilla
-
-using namespace mozilla::places;
 
 // nsNavHistoryResultNode ******************************************************
 
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryResultNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNavHistoryResultNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNavHistoryResultNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mParent, nsINavHistoryContainerResultNode);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResultNode)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResultNode)
-  if (aIID.Equals(NS_GET_IID(nsIClassInfo)))
-    foundInterface = static_cast<nsIClassInfo *>(&mozilla::places::sResultNodeClassInfo);
-  else
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultNode)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsNavHistoryResultNode, nsINavHistoryResultNode)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsNavHistoryResultNode, nsINavHistoryResultNode)
 
 nsNavHistoryResultNode::nsNavHistoryResultNode(
     const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount,
@@ -225,33 +116,32 @@ nsNavHistoryResultNode::nsNavHistoryResu
   mTitle(aTitle),
   mAccessCount(aAccessCount),
   mTime(aTime),
   mFaviconURI(aIconURI),
   mBookmarkIndex(-1),
   mItemId(-1),
   mDateAdded(0),
   mLastModified(0),
-  mIndentLevel(-1)
+  mIndentLevel(-1),
+  mViewIndex(-1)
 {
   mTags.SetIsVoid(PR_TRUE);
 }
 
 NS_IMETHODIMP
-nsNavHistoryResultNode::GetIcon(nsACString& aIcon)
+nsNavHistoryResultNode::GetIcon(nsIURI** aURI)
 {
   if (mFaviconURI.IsEmpty()) {
-    aIcon.Truncate();
+    *aURI = nsnull;
     return NS_OK;
   }
-
   nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
   NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
-  faviconService->GetFaviconSpecForIconString(mFaviconURI, aIcon);
-  return NS_OK;
+  return faviconService->GetFaviconLinkForIconString(mFaviconURI, aURI);
 }
 
 NS_IMETHODIMP
 nsNavHistoryResultNode::GetParent(nsINavHistoryContainerResultNode** aParent)
 {
   NS_IF_ADDREF(*aParent = mParent);
   return NS_OK;
 }
@@ -317,24 +207,34 @@ nsNavHistoryResultNode::GetTags(nsAStrin
       nsNavHistoryResult* result = query->GetResult();
       NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
       result->AddAllBookmarksObserver(query);
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNavHistoryResultNode::GetPropertyBag(nsIWritablePropertyBag** aBag)
+{
+  nsNavHistoryResult* result = GetResult();
+  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
+  return result->PropertyBagFor(this, aBag);
+}
+
+
 // nsNavHistoryResultNode::OnRemoving
 //
 //    This will zero out some values in case somebody still holds a reference
 
 void
 nsNavHistoryResultNode::OnRemoving()
 {
   mParent = nsnull;
+  mViewIndex = -1;
 }
 
 
 // nsNavHistoryResultNode::GetResult
 //
 //    This will find the result for this node. We can ask the nearest container
 //    for this value (either ourselves or our parents should be a container,
 //    and all containers have result pointers).
@@ -496,40 +396,40 @@ nsNavHistoryContainerResultNode::OnRemov
   nsNavHistoryResultNode::OnRemoving();
   for (PRInt32 i = 0; i < mChildren.Count(); i ++)
     mChildren[i]->OnRemoving();
   mChildren.Clear();
 }
 
 
 // nsNavHistoryContainerResultNode::AreChildrenVisible
+//
+//    Folders can't always depend on their mViewIndex value to determine if
+//    their children are visible because they can be root nodes. Root nodes
+//    are visible if a tree is attached to the result.
 
 PRBool
 nsNavHistoryContainerResultNode::AreChildrenVisible()
 {
+  // can't see children when we're invisible
+  if (! mExpanded)
+    return PR_FALSE;
+
+  // easy case, the node itself is visible
+  if (mViewIndex >= 0)
+    return PR_TRUE;
+
   nsNavHistoryResult* result = GetResult();
-  if (!result) {
+  if (! result) {
     NS_NOTREACHED("Invalid result");
     return PR_FALSE;
   }
-
-  // can't see children when we're invisible
-  if (!mExpanded)
-    return PR_FALSE;
-
-  // Now check if any ancestor is closed.
-  nsNavHistoryContainerResultNode* ancestor = mParent;
-  while (ancestor) {
-    if (!ancestor->mExpanded)
-      return PR_FALSE;
-
-    ancestor = ancestor->mParent;
-  }
-
-  return PR_TRUE;
+  if (result->mRootNode == this && result->mView)
+    return PR_TRUE;
+  return PR_FALSE;
 }
 
 
 // nsNavHistoryContainerResultNode::GetContainerOpen
 
 NS_IMETHODIMP
 nsNavHistoryContainerResultNode::GetContainerOpen(PRBool *aContainerOpen)
 {
@@ -698,49 +598,43 @@ nsNavHistoryContainerResultNode::FillSta
 //    rather unusual case: we don't often delete things, and we usually don't
 //    even show the last visit date for folders. Updating would be slower
 //    because we would have to recompute it from scratch.
 
 void
 nsNavHistoryContainerResultNode::ReverseUpdateStats(PRInt32 aAccessCountChange)
 {
   if (mParent) {
-    nsNavHistoryResult* result = GetResult();
-    PRBool shouldUpdateView = result && result->GetView() &&
-                              mParent->mParent &&
-                              mParent->mParent->AreChildrenVisible();
-
     mParent->mAccessCount += aAccessCountChange;
     PRBool timeChanged = PR_FALSE;
     if (mTime > mParent->mTime) {
       timeChanged = PR_TRUE;
       mParent->mTime = mTime;
     }
 
-    if (shouldUpdateView) {
-      result->GetView()->NodeHistoryDetailsChanged(
-        static_cast<nsINavHistoryContainerResultNode*>(mParent),
-        mParent->mTime,
-        mParent->mAccessCount);
-    }
-
     // check sorting, the stats may have caused this node to move if the
     // sorting depended on something we are changing.
     PRUint16 sortMode = mParent->GetSortType();
-    PRBool sortingByVisitCount =
-      sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING ||
-      sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING;
-    PRBool sortingByTime =
-      sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING ||
-      sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING;
-
-    if ((sortingByVisitCount && aAccessCountChange != 0) ||
-        (sortingByTime && timeChanged)) {
+    PRBool resorted = PR_FALSE;
+    if (((sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING ||
+          sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING) &&
+         aAccessCountChange != 0) ||
+        ((sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING ||
+          sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) &&
+         timeChanged)) {
+
       PRUint32 ourIndex = mParent->FindChild(this);
-      EnsureItemPosition(ourIndex);
+      resorted = EnsureItemPosition(ourIndex);
+    }
+    if (!resorted) {
+      // repaint visible rows
+      nsNavHistoryResult* result = GetResult();
+      if (result && result->GetView() && mParent->AreChildrenVisible()) {
+        result->GetView()->ItemChanged(static_cast<nsINavHistoryContainerResultNode*>(mParent));
+      }
     }
 
     mParent->ReverseUpdateStats(aAccessCountChange);
   }
 }
 
 
 // nsNavHistoryContainerResultNode::GetSortType
@@ -1339,16 +1233,39 @@ nsNavHistoryContainerResultNode::FindChi
         *aNodeIndex = i;
         return mChildren[i];
       }
     }
   }
   return nsnull;
 }
 
+
+// nsNavHistoryContainerResultNode::FindChildFolder
+//
+//    Searches this folder for the given subfolder. Returns null if not found.
+//    DOES NOT ADDREF.
+
+nsNavHistoryFolderResultNode*
+nsNavHistoryContainerResultNode::FindChildFolder(PRInt64 aFolderId,
+    PRUint32* aNodeIndex)
+{
+  for (PRInt32 i = 0; i < mChildren.Count(); i ++) {
+    if (mChildren[i]->IsFolder()) {
+      nsNavHistoryFolderResultNode* folder = mChildren[i]->GetAsFolder();
+      if (folder->mItemId == aFolderId) {
+        *aNodeIndex = i;
+        return folder;
+      }
+    }
+  }
+  return nsnull;
+}
+
+
 //  nsNavHistoryContainerResultNode::FindChildContainerByName
 //
 //    Searches this container for a subfolder with the given name. This is used
 //    to find host and "day" nodes. Returns null if not found. DOES NOT ADDREF.
 
 nsNavHistoryContainerResultNode*
 nsNavHistoryContainerResultNode::FindChildContainerByName(
     const nsACString& aTitle, PRUint32* aNodeIndex)
@@ -1381,16 +1298,17 @@ nsNavHistoryContainerResultNode::FindChi
 nsresult
 nsNavHistoryContainerResultNode::InsertChildAt(nsNavHistoryResultNode* aNode,
                                                PRInt32 aIndex,
                                                PRBool aIsTemporary)
 {
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
+  aNode->mViewIndex = -1;
   aNode->mParent = this;
   aNode->mIndentLevel = mIndentLevel + 1;
   if (! aIsTemporary && aNode->IsContainer()) {
     // need to update all the new item's children
     nsNavHistoryContainerResultNode* container = aNode->GetAsContainer();
     container->mResult = mResult;
     container->FillStats();
   }
@@ -1399,27 +1317,26 @@ nsNavHistoryContainerResultNode::InsertC
     return NS_ERROR_OUT_OF_MEMORY;
 
   // update our (the container) stats and refresh our row on the screen
   if (! aIsTemporary) {
     mAccessCount += aNode->mAccessCount;
     if (mTime < aNode->mTime)
       mTime = aNode->mTime;
     if (result->GetView() && (!mParent || mParent->AreChildrenVisible()))
-      result->GetView()->NodeHistoryDetailsChanged(
-          static_cast<nsINavHistoryContainerResultNode*>(this), mTime,
-          mAccessCount);
+      result->GetView()->ItemChanged(
+          static_cast<nsINavHistoryContainerResultNode*>(this));
     ReverseUpdateStats(aNode->mAccessCount);
   }
 
   // Update tree if we are visible. Note that we could be here and not expanded,
   // like when there is a bookmark folder being updated because its parent is
   // visible.
   if (result->GetView() && AreChildrenVisible())
-    result->GetView()->NodeInserted(this, aNode, aIndex);
+    result->GetView()->ItemInserted(this, aNode, aIndex);
   return NS_OK;
 }
 
 
 // nsNavHistoryContainerResultNode::InsertSortedChild
 //
 //    This locates the proper place for insertion according to the current sort
 //    and calls InsertChildAt
@@ -1490,17 +1407,17 @@ nsNavHistoryContainerResultNode::EnsureI
   PRUint32 newIndex = FindInsertionPoint(
                           node, comparator,sortAnno.get(), nsnull);
   mChildren.InsertObjectAt(node.get(), newIndex);
 
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, PR_TRUE);
 
   if (result->GetView() && AreChildrenVisible())
-    result->GetView()->NodeMoved(node, this, aIndex, this, newIndex);
+    result->GetView()->ItemMoved(node, this, aIndex, this, newIndex);
 
   return PR_TRUE;
 }
 
 // nsNavHistoryContainerResultNode::MergeResults
 //
 //    This takes a list of nodes and merges them into the current result set.
 //    Any containers that are added must already be sorted.
@@ -1605,17 +1522,17 @@ nsNavHistoryContainerResultNode::Replace
   // actually replace
   if (! mChildren.ReplaceObjectAt(aNode, aIndex))
     return NS_ERROR_FAILURE;
 
   // update view
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
   if (result->GetView() && AreChildrenVisible())
-    result->GetView()->NodeReplaced(this, oldItem, aNode, aIndex);
+    result->GetView()->ItemReplaced(this, oldItem, aNode, aIndex);
 
   mChildren[aIndex]->OnRemoving();
   return NS_OK;
 }
 
 
 // nsNavHistoryContainerResultNode::RemoveChildAt
 //
@@ -1646,17 +1563,17 @@ nsNavHistoryContainerResultNode::RemoveC
     oldAccessCount = mAccessCount;
     mAccessCount -= mChildren[aIndex]->mAccessCount;
     NS_ASSERTION(mAccessCount >= 0, "Invalid access count while updating!");
   }
 
   // remove from our list and notify the tree
   mChildren.RemoveObjectAt(aIndex);
   if (result->GetView() && AreChildrenVisible())
-    result->GetView()->NodeRemoved(this, oldNode, aIndex);
+    result->GetView()->ItemRemoved(this, oldNode, aIndex);
 
   if (! aIsTemporary) {
     ReverseUpdateStats(mAccessCount - oldAccessCount);
     oldNode->OnRemoving();
   }
   return NS_OK;
 }
 
@@ -1700,17 +1617,17 @@ nsNavHistoryContainerResultNode::Recursi
 //    Normally you want this to be true, but it can be false if the thing you
 //    are changing can not affect sorting (like favicons).
 //
 //    You should NOT change any child lists as part of the callback function.
 
 void
 nsNavHistoryContainerResultNode::UpdateURIs(PRBool aRecursive, PRBool aOnlyOne,
     PRBool aUpdateSort, const nsCString& aSpec,
-    void (*aCallback)(nsNavHistoryResultNode*,void*, nsNavHistoryResult*), void* aClosure)
+    void (*aCallback)(nsNavHistoryResultNode*,void*), void* aClosure)
 {
   nsNavHistoryResult* result = GetResult();
   if (! result) {
     NS_NOTREACHED("Must have a result for this query");
     return;
   }
 
   // this needs to be owning since sometimes we remove and re-insert nodes
@@ -1741,64 +1658,57 @@ nsNavHistoryContainerResultNode::UpdateU
   for (PRInt32 i = 0; i < matches.Count(); i ++)
   {
     nsNavHistoryResultNode* node = matches[i];
     nsNavHistoryContainerResultNode* parent = node->mParent;
     if (! parent) {
       NS_NOTREACHED("All URI nodes being updated must have parents");
       continue;
     }
+    PRBool childrenVisible = result->GetView() != nsnull && parent->AreChildrenVisible();
 
     PRUint32 oldAccessCount = node->mAccessCount;
     PRTime oldTime = node->mTime;
-    aCallback(node, aClosure, result);
-
-    PRBool childrenVisible = result->GetView() != nsnull && parent->AreChildrenVisible();
+    aCallback(node, aClosure);
 
     if (oldAccessCount != node->mAccessCount || oldTime != node->mTime) {
       // need to update/redraw the parent
       parent->mAccessCount += node->mAccessCount - oldAccessCount;
       if (node->mTime > parent->mTime)
         parent->mTime = node->mTime;
       if (childrenVisible)
-        result->GetView()->NodeHistoryDetailsChanged(
-            static_cast<nsINavHistoryContainerResultNode*>(parent),
-            parent->mTime,
-            parent->mAccessCount);
+        result->GetView()->ItemChanged(
+            static_cast<nsINavHistoryContainerResultNode*>(parent));
       parent->ReverseUpdateStats(node->mAccessCount - oldAccessCount);
     }
 
     if (aUpdateSort) {
       PRInt32 childIndex = parent->FindChild(node);
-      NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to");
-      if (childIndex >= 0)
-        parent->EnsureItemPosition(childIndex);
+      if ((childIndex < 0 || !parent->EnsureItemPosition(childIndex) && childrenVisible)) {
+        result->GetView()->ItemChanged(node);
+      }
+    } else if (childrenVisible) {
+      result->GetView()->ItemChanged(node);
     }
   }
 }
 
 
 // nsNavHistoryContainerResultNode::ChangeTitles
 //
 //    This is used to update the titles in the tree. This is called from both
 //    query and bookmark folder containers to update the tree. Bookmark folders
 //    should be sure to set recursive to false, since child folders will have
 //    their own callbacks registered.
 
 static void setTitleCallback(
-    nsNavHistoryResultNode* aNode, void* aClosure,
-    nsNavHistoryResult* aResult)
+    nsNavHistoryResultNode* aNode, void* aClosure)
 {
   const nsACString* newTitle = reinterpret_cast<nsACString*>(aClosure);
   aNode->mTitle = *newTitle;
-
-  if (aResult && aResult->GetView() &&
-      (!aNode->mParent || aNode->mParent->AreChildrenVisible())) {
-    aResult->GetView()->NodeTitleChanged(aNode, *newTitle);
-  }
 }
 nsresult
 nsNavHistoryContainerResultNode::ChangeTitles(nsIURI* aURI,
                                               const nsACString& aNewTitle,
                                               PRBool aRecursive,
                                               PRBool aOnlyOne)
 {
   // uri string
@@ -2956,26 +2866,20 @@ nsNavHistoryQueryResultNode::OnClearHist
   return NS_OK;
 }
 
 
 // nsNavHistoryQueryResultNode::OnPageChanged
 //
 
 static void setFaviconCallback(
-   nsNavHistoryResultNode* aNode, void* aClosure,
-   nsNavHistoryResult* aResult)
+   nsNavHistoryResultNode* aNode, void* aClosure)
 {
   const nsCString* newFavicon = static_cast<nsCString*>(aClosure);
   aNode->mFaviconURI = *newFavicon;
-
-  if (aResult && aResult->GetView() &&
-      (!aNode->mParent || aNode->mParent->AreChildrenVisible())) {
-    aResult->GetView()->NodeIconChanged(aNode);
-  }
 }
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnPageChanged(nsIURI *aURI, PRUint32 aWhat,
                          const nsAString &aValue)
 {
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
@@ -3008,107 +2912,96 @@ nsNavHistoryQueryResultNode::OnPageChang
 
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnPageExpired(nsIURI* aURI, PRTime aVisitTime,
                                            PRBool aWholeEntry)
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNavHistoryQueryResultNode::OnItemAdded(PRInt64 aItemId,
+                                         PRInt64 aFolder,
+                                         PRInt32 aIndex)
+{
+  nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
+  NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
+  PRUint16 itemType;
+  nsresult rv = bookmarks->GetItemType(aItemId, &itemType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return OnItemAdded(aItemId, aFolder, aIndex, itemType);
+}
+
 // nsNavHistoryQueryResultNode bookmark observers
 //
 //    These are the bookmark observer functions for query nodes. They listen
 //    for bookmark events and refresh the results if we have any dependence on
 //    the bookmark system.
 
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnItemAdded(PRInt64 aItemId,
                                          PRInt64 aFolder,
                                          PRInt32 aIndex,
                                          PRUint16 aItemType)
 {
-  if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
-      mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
+  if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
     return Refresh();
   return NS_OK;
 }
-
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::OnBeforeItemRemoved(PRInt64 aItemId,
-                                                 PRUint16 aItemType)
+nsNavHistoryQueryResultNode::OnBeforeItemRemoved(PRInt64 aItemId)
 {
   return NS_OK;
 }
-
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnItemRemoved(PRInt64 aItemId, PRInt64 aFolder,
-                                           PRInt32 aIndex, PRUint16 aItemType)
+                                            PRInt32 aIndex)
 {
   if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
     return Refresh();
   return NS_OK;
 }
-
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnItemChanged(PRInt64 aItemId,
                                            const nsACString& aProperty,
                                            PRBool aIsAnnotationProperty,
-                                           const nsACString& aNewValue,
-                                           PRTime aLastModified,
-                                           PRUint16 aItemType)
+                                           const nsACString& aValue)
 {
   // History observers should not get OnItemChanged
   // but should get the corresponding history notifications instead.
   // For bookmark queries, "all bookmark" observers should get OnItemChanged.
   // For example, when a title of a bookmark changes, we want that to refresh.
-
-  if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) {
-    // Make sure it's not a folder or a separator.
-    if (aItemType != nsINavBookmarksService::TYPE_BOOKMARK)
-      return NS_OK;
-
+  if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
     (void)Refresh();
-  }
-  else {
+  else
     NS_WARNING("history observers should not get OnItemChanged, but should get the corresponding history notifications instead");
-  }
 
   return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
                                                aIsAnnotationProperty,
-                                               aNewValue,
-                                               aLastModified,
-                                               aItemType);
+                                               aValue);
 }
 
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnItemVisited(PRInt64 aItemId,
                                            PRInt64 aVisitId, PRTime aTime)
 {
   // for bookmark queries, "all bookmark" observer should get OnItemVisited
   // but it is ignored.
   if (mLiveUpdate != QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
     NS_WARNING("history observers should not get OnItemVisited, but should get OnVisit instead");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::OnItemMoved(PRInt64 aFolder,
-                                         PRInt64 aOldParent, PRInt32 aOldIndex,
-                                         PRInt64 aNewParent, PRInt32 aNewIndex,
-                                         PRUint16 aItemType)
+nsNavHistoryQueryResultNode::OnItemMoved(PRInt64 aFolder, PRInt64 aOldParent,
+                                            PRInt32 aOldIndex, PRInt64 aNewParent,
+                                            PRInt32 aNewIndex)
 {
-  // 1. The query cannot be affected by the item's position
-  // 2. For the time being, we cannot optimize this not to update
-  //    queries which are not restricted to some folders, due to way
-  //    sub-queries are updated (see Refresh)
-  if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS &&
-      aItemType != nsINavBookmarksService::TYPE_SEPARATOR &&
-      aOldParent != aNewParent) {
+  if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
     return Refresh();
-  }
   return NS_OK;
 }
 
 // nsNavHistoryFolderResultNode ************************************************
 //
 //    HOW DYNAMIC FOLDER UPDATING WORKS
 //
 //    When you create a result, it will automatically keep itself in sync with
@@ -3561,16 +3454,31 @@ nsNavHistoryFolderResultNode::OnBeginUpd
 // nsNavHistoryFolderResultNode::OnEndUpdateBatch (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnEndUpdateBatch()
 {
   return NS_OK;
 }
 
+
+NS_IMETHODIMP
+nsNavHistoryFolderResultNode::OnItemAdded(PRInt64 aItemId,
+                                         PRInt64 aFolder,
+                                         PRInt32 aIndex)
+{
+  nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
+  NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
+  PRUint16 itemType;
+  nsresult rv = bookmarks->GetItemType(aItemId, &itemType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return OnItemAdded(aItemId, aFolder, aIndex, itemType);
+}
+
+
 // nsNavHistoryFolderResultNode::OnItemAdded (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnItemAdded(PRInt64 aItemId,
                                           PRInt64 aParentFolder,
                                           PRInt32 aIndex,
                                           PRUint16 aItemType)
 {
@@ -3656,30 +3564,28 @@ nsNavHistoryFolderResultNode::OnItemAdde
   // insert at sorted position
   return InsertSortedChild(node, PR_FALSE);
 }
 
 
 // nsNavHistoryFolderResultNode::OnBeforeItemRemoved (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
-nsNavHistoryFolderResultNode::OnBeforeItemRemoved(PRInt64 aItemId,
-                                                  PRUint16 aItemType)
+nsNavHistoryFolderResultNode::OnBeforeItemRemoved(PRInt64 aItemId)
 {
   return NS_OK;
 }
 
 
 // nsNavHistoryFolderResultNode::OnItemRemoved (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnItemRemoved(PRInt64 aItemId,
                                             PRInt64 aParentFolder,
-                                            PRInt32 aIndex,
-                                            PRUint16 aItemType)
+                                            PRInt32 aIndex)
 {
   // We only care about notifications when a child changes. When the deleted
   // item is us, our parent should also be registered and will remove us from
   // its list.
   if (mItemId == aItemId)
     return NS_OK;
 
   NS_ASSERTION(aParentFolder == mItemId, "Got wrong bookmark update");
@@ -3719,80 +3625,71 @@ nsNavHistoryFolderResultNode::OnItemRemo
 
 
 // nsNavHistoryResultNode::OnItemChanged
 
 NS_IMETHODIMP
 nsNavHistoryResultNode::OnItemChanged(PRInt64 aItemId,
                                       const nsACString& aProperty,
                                       PRBool aIsAnnotationProperty,
-                                      const nsACString& aNewValue,
-                                      PRTime aLastModified,
-                                      PRUint16 aItemType)
+                                      const nsACString& aValue)
 {
   if (aItemId != mItemId)
     return NS_OK;
 
-  mLastModified = aLastModified;
+  if (aProperty.EqualsLiteral("title")) {
+    // XXX: what should we do if the new title is void?
+    mTitle = aValue;
+  }
+  else if (aProperty.EqualsLiteral("uri")) {
+    mURI = aValue;
+    // clear the tags string as well
+    mTags.SetIsVoid(PR_TRUE);
+  }
+  else if (aProperty.EqualsLiteral("favicon")) {
+    mFaviconURI = aValue;
+  }
+  else if (aProperty.EqualsLiteral("cleartime")) {
+    mTime = 0;
+  }
+  else if (aProperty.EqualsLiteral("tags")) {
+    mTags.SetIsVoid(PR_TRUE);
+  }
+  else if (!aProperty.EqualsLiteral("keyword") &&
+           !aProperty.EqualsLiteral("dateAdded") &&
+           !aProperty.EqualsLiteral("lastModified") && !aIsAnnotationProperty) {
+    NS_NOTREACHED("Unknown bookmark property changing.");
+  }
+
+  nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
+  NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);
+
+  PRTime lastModified;
+  nsresult rv = bookmarks->GetItemLastModified(aItemId, &lastModified);
+  if (NS_SUCCEEDED(rv)) {
+    mLastModified = lastModified;
+  }
+  else {
+    mLastModified = 0;
+  }
+
+  PRTime dateAdded;
+  rv = bookmarks->GetItemDateAdded(aItemId, &dateAdded);
+  if (NS_SUCCEEDED(rv)) {
+    mDateAdded = dateAdded;
+  }
+  else {
+    mDateAdded = 0;
+  }
 
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
-  PRBool shouldUpdateView =
-    result->GetView() && (!mParent || mParent->AreChildrenVisible());
-
-  if (aIsAnnotationProperty) {
-    if (shouldUpdateView)
-      result->GetView()->NodeAnnotationChanged(this, aProperty);
-  }
-  else if (aProperty.EqualsLiteral("title")) {
-    // XXX: what should we do if the new title is void?
-    mTitle = aNewValue;
-    if (shouldUpdateView)
-      result->GetView()->NodeTitleChanged(this, mTitle);
-  }
-  else if (aProperty.EqualsLiteral("uri")) {
-    // clear the tags string as well
-    mTags.SetIsVoid(PR_TRUE);
-    mURI = aNewValue;
-    if (shouldUpdateView)
-      result->GetView()->NodeURIChanged(this, mURI);
-  }
-  else if (aProperty.EqualsLiteral("favicon")) {
-    mFaviconURI = aNewValue;
-    if (shouldUpdateView)
-      result->GetView()->NodeIconChanged(this);
-  }
-  else if (aProperty.EqualsLiteral("cleartime")) {
-    mTime = 0;
-    if (shouldUpdateView)
-      result->GetView()->NodeHistoryDetailsChanged(this, 0, mAccessCount);
-  }
-  else if (aProperty.EqualsLiteral("tags")) {
-    mTags.SetIsVoid(PR_TRUE);
-    if (shouldUpdateView)
-      result->GetView()->NodeTagsChanged(this);
-  }
-  else if (aProperty.EqualsLiteral("dateAdded")) {
-    // aNewValue has the date as a string, but we can use aLastModified,
-    // because it's set to the same value when dateAdded is changed.
-    mDateAdded = aLastModified;
-    if (shouldUpdateView)
-      result->GetView()->NodeDateAddedChanged(this, mDateAdded);
-  }
-  else if (aProperty.EqualsLiteral("lastModified")) {
-    if (shouldUpdateView)
-      result->GetView()->NodeLastModifiedChanged(this, aLastModified);
-  }
-  else if (aProperty.EqualsLiteral("keyword")) {
-    if (shouldUpdateView)
-      result->GetView()->NodeKeywordChanged(this, aNewValue);
-  }
-  else {
-    NS_NOTREACHED("Unknown bookmark property changing.");
+  if (result->GetView() && (!mParent || mParent->AreChildrenVisible())) {
+    result->GetView()->ItemChanged(this);
   }
 
   if (!mParent)
     return NS_OK;
 
   // DO NOT OPTIMIZE THIS TO CHECK aProperty
   // the sorting methods fall back to each other so we need to re-sort the
   // result even if it's not set to sort by the given property
@@ -3801,97 +3698,90 @@ nsNavHistoryResultNode::OnItemChanged(PR
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnItemChanged(PRInt64 aItemId,
                                             const nsACString& aProperty,
                                             PRBool aIsAnnotationProperty,
-                                            const nsACString& aNewValue,
-                                            PRTime aLastModified,
-                                            PRUint16 aItemType) {
+                                            const nsACString& aValue) {
   // The query-item's title is used for simple-query nodes
   if (mQueryItemId != -1) {
     PRBool isTitleChange = aProperty.EqualsLiteral("title");
     if ((mQueryItemId == aItemId && !isTitleChange) ||
         (mQueryItemId != aItemId && isTitleChange)) {
       return NS_OK;
     }
   }
 
   return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
                                                aIsAnnotationProperty,
-                                               aNewValue,
-                                               aLastModified,
-                                               aItemType);
+                                               aValue);
 }
 
 // nsNavHistoryFolderResultNode::OnItemVisited (nsINavBookmarkObserver)
 //
 //    Update visit count and last visit time and refresh.
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnItemVisited(PRInt64 aItemId,
                                             PRInt64 aVisitId, PRTime aTime)
 {
   PRBool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) ||
                         (mParent && mParent->mOptions->ExcludeItems()) ||
                         mOptions->ExcludeItems();
   if (excludeItems)
     return NS_OK; // don't update items when we aren't displaying them
-  if (!StartIncrementalUpdate())
+  if (! StartIncrementalUpdate())
     return NS_OK;
 
   PRUint32 nodeIndex;
   nsNavHistoryResultNode* node = FindChildById(aItemId, &nodeIndex);
-  if (!node)
+  if (! node)
     return NS_ERROR_FAILURE;
 
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
   // update node
   node->mTime = aTime;
   node->mAccessCount ++;
 
   // update us
   PRInt32 oldAccessCount = mAccessCount;
   mAccessCount ++;
   if (aTime > mTime)
     mTime = aTime;
   ReverseUpdateStats(mAccessCount - oldAccessCount);
 
-  if (result->GetView() && AreChildrenVisible()) {
-    // Sorting has not changed, just redraw the row if it's visible.
-    result->GetView()->NodeHistoryDetailsChanged(node, mTime, mAccessCount);
-  }
-
   // update sorting if necessary
   PRUint32 sortType = GetSortType();
   if (sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING ||
       sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING ||
       sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING ||
       sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) {
     PRInt32 childIndex = FindChild(node);
     NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to");
     if (childIndex >= 0) {
       EnsureItemPosition(childIndex);
     }
+  } else if (result->GetView() && AreChildrenVisible()) {
+    // no sorting changed, just redraw the row if visible
+    result->GetView()->ItemChanged(node);
   }
-
   return NS_OK;
 }
 
 // nsNavHistoryFolderResultNode::OnItemMoved (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryFolderResultNode::OnItemMoved(PRInt64 aItemId, PRInt64 aOldParent,
                                           PRInt32 aOldIndex, PRInt64 aNewParent,
-                                          PRInt32 aNewIndex, PRUint16 aItemType)
+                                          PRInt32 aNewIndex)
 {
   NS_ASSERTION(aOldParent == mItemId || aNewParent == mItemId,
                "Got a bookmark message that doesn't belong to us");
   if (! StartIncrementalUpdate())
     return NS_OK; // entire container was refreshed for us
 
   if (aOldParent == aNewParent) {
     // getting moved within the same folder, we don't want to do a remove and
@@ -3912,19 +3802,19 @@ nsNavHistoryFolderResultNode::OnItemMove
     node->mBookmarkIndex = aNewIndex;
 
     // adjust position
     EnsureItemPosition(index);
     return NS_OK;
   } else {
     // moving between two different folders, just do a remove and an add
     if (aOldParent == mItemId)
-      OnItemRemoved(aItemId, aOldParent, aOldIndex, aItemType);
+      OnItemRemoved(aItemId, aOldParent, aOldIndex);
     if (aNewParent == mItemId)
-      OnItemAdded(aItemId, aNewParent, aNewIndex, aItemType);
+      OnItemAdded(aItemId, aNewParent, aNewIndex);
   }
   return NS_OK;
 }
 
 
 // nsNavHistorySeparatorResultNode
 //
 // Separator nodes do not hold any data
@@ -3968,20 +3858,34 @@ TraverseBookmarkFolderObservers(nsTrimIn
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
                                        "mBookmarkFolderObservers value[i]");
     nsNavHistoryResultNode* node = aData->ElementAt(i);
     cb->NoteXPCOMChild(node);
   }
   return PL_DHASH_NEXT;
 }
 
+static PLDHashOperator
+TraversePropertyBags(nsISupportsHashKey::KeyType aKey,
+                     nsCOMPtr<nsIWritablePropertyBag> &aData,
+                     void *aClosure)
+{
+  nsCycleCollectionTraversalCallback* cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mPropertyBags key");
+  cb->NoteXPCOMChild(aKey);
+
+  return PL_DHASH_NEXT;
+}
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNavHistoryResult)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRootNode, nsINavHistoryContainerResultNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mView)
   tmp->mBookmarkFolderObservers.Enumerate(&TraverseBookmarkFolderObservers, &cb);
+  tmp->mPropertyBags.Enumerate(&TraversePropertyBags, &cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAllBookmarksObservers, nsNavHistoryQueryResultNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mHistoryObservers, nsNavHistoryQueryResultNode)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResult)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResult)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResult)
@@ -4054,16 +3958,17 @@ nsNavHistoryResult::Init(nsINavHistoryQu
       return NS_ERROR_OUT_OF_MEMORY;
   }
   rv = aOptions->Clone(getter_AddRefs(mOptions));
   NS_ENSURE_SUCCESS(rv, rv);
   mSortingMode = aOptions->SortingMode();
   rv = aOptions->GetSortingAnnotation(mSortingAnnotation);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  mPropertyBags.Init();
   if (! mBookmarkFolderObservers.Init(128))
     return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ASSERTION(mRootNode->mIndentLevel == -1,
                "Root node's indent level initialized wrong");
   mRootNode->FillStats();
 
   return NS_OK;
@@ -4095,16 +4000,48 @@ nsNavHistoryResult::NewHistoryResult(nsI
   // Correctly set mBatchInProgress for the result based on the root node value.
   if (aRoot->IsQuery())
     (*result)->mBatchInProgress = aRoot->GetAsQuery()->mBatchInProgress;
 
   return NS_OK;
 }
 
 
+// nsNavHistoryResult::PropertyBagFor
+//
+//    Given a pointer to a result node, this will give you the property bag
+//    corresponding to it. Each node exposes a property bag to be used to
+//    store temporary data. It is designed primarily for those implementing
+//    container sources for storing data they need.
+//
+//    Since we expect very few result nodes will ever have their property bags
+//    used, and since we can have a LOT of result nodes, we store the property
+//    bags separately in a hash table in the parent result.
+//
+//    This function is called by a node when somebody wants the property bag.
+//    It will create a property bag if necessary and store it for later
+//    retrieval.
+
+nsresult
+nsNavHistoryResult::PropertyBagFor(nsISupports* aObject,
+                                   nsIWritablePropertyBag** aBag)
+{
+  *aBag = nsnull;
+  if (mPropertyBags.Get(aObject, aBag) && *aBag)
+    return NS_OK;
+
+  nsresult rv = NS_NewHashPropertyBag(aBag);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (! mPropertyBags.Put(aObject, *aBag)) {
+    NS_RELEASE(*aBag);
+    *aBag = nsnull;
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  return NS_OK;
+}
 
 // nsNavHistoryResult::AddHistoryObserver
 
 void
 nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode)
 {
   if (! mIsHistoryObserver) {
       nsNavHistory* history = nsNavHistory::GetHistoryService();
@@ -4251,17 +4188,17 @@ nsNavHistoryResult::SetSortingMode(PRUin
   if (comparator) {
     nsNavHistory* history = nsNavHistory::GetHistoryService();
     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
     mRootNode->RecursiveSort(mSortingAnnotation.get(), comparator);
   }
 
   if (mView) {
     mView->SortingChanged(aSortingMode);
-    mView->InvalidateContainer(mRootNode);
+    mView->InvalidateAll();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistoryResult::GetSortingAnnotation(nsACString& _result) {
   _result.Assign(mSortingAnnotation);
   return NS_OK;
@@ -4363,69 +4300,71 @@ nsNavHistoryResult::OnEndUpdateBatch()
   return NS_OK;
 }
 
 
 // nsNavHistoryResult::OnItemAdded (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnItemAdded(PRInt64 aItemId,
-                                PRInt64 aParentId,
-                                PRInt32 aIndex,
-                                PRUint16 aItemType)
+                                PRInt64 aFolder,
+                                PRInt32 aIndex)
 {
-  ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
-      OnItemAdded(aItemId, aParentId, aIndex, aItemType));
-  ENUMERATE_HISTORY_OBSERVERS(OnItemAdded(aItemId, aParentId, aIndex, aItemType));
-  ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemAdded(aItemId, aParentId, aIndex,
-                                                aItemType));
+  nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
+  NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
+
+  PRUint16 itemType;
+  nsresult rv = bookmarks->GetItemType(aItemId, &itemType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aFolder,
+      OnItemAdded(aItemId, aFolder, aIndex, itemType));
+  ENUMERATE_HISTORY_OBSERVERS(OnItemAdded(aItemId, aFolder, aIndex, itemType));
+  ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemAdded(aItemId, aFolder, aIndex,
+                                                itemType));
   return NS_OK;
 }
 
 
 // nsNavHistoryResult::OnBeforeItemRemoved (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
-nsNavHistoryResult::OnBeforeItemRemoved(PRInt64 aItemId, PRUint16 aItemType)
+nsNavHistoryResult::OnBeforeItemRemoved(PRInt64 aItemId)
 {
   // Nobody actually does anything with this method, so we do not need to notify
   return NS_OK;
 }
 
 
 // nsNavHistoryResult::OnItemRemoved (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnItemRemoved(PRInt64 aItemId,
-                                  PRInt64 aParentId, PRInt32 aIndex,
-                                  PRUint16 aItemType)
+                                  PRInt64 aFolder, PRInt32 aIndex)
 {
-  ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
-      OnItemRemoved(aItemId, aParentId, aIndex, aItemType));
+  ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aFolder,
+      OnItemRemoved(aItemId, aFolder, aIndex));
   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
-      OnItemRemoved(aItemId, aParentId, aIndex, aItemType));
+      OnItemRemoved(aItemId, aFolder, aIndex));
   ENUMERATE_HISTORY_OBSERVERS(
-      OnItemRemoved(aItemId, aParentId, aIndex, aItemType));
+      OnItemRemoved(aItemId, aFolder, aIndex));
   return NS_OK;
 }
 
 
 // nsNavHistoryResult::OnItemChanged (nsINavBookmarkObserver)
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnItemChanged(PRInt64 aItemId,
                                   const nsACString &aProperty,
                                   PRBool aIsAnnotationProperty,
-                                  const nsACString &aNewValue,
-                                  PRTime aLastModified,
-                                  PRUint16 aItemType)
+                                  const nsACString &aValue)
 {
   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
-    OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue,
-                  aLastModified, aItemType));
+    OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aValue));
 
   // Note: folder-nodes set their own bookmark observer only once they're
   // opened, meaning we cannot optimize this code path for changes done to
   // folder-nodes.
 
   nsNavBookmarks* bookmarkService = nsNavBookmarks::GetBookmarksService();
   NS_ENSURE_TRUE(bookmarkService, NS_ERROR_OUT_OF_MEMORY);
 
@@ -4445,18 +4384,17 @@ nsNavHistoryResult::OnItemChanged(PRInt6
       nsRefPtr<nsNavHistoryResultNode> node =
         folder->FindChildById(aItemId, &nodeIndex);
       // if ExcludeItems is true we don't update non visible items
       PRBool excludeItems = (mRootNode->mOptions->ExcludeItems()) ||
                              folder->mOptions->ExcludeItems();
       if (node &&
           (!excludeItems || !(node->IsURI() || node->IsSeparator())) &&
           folder->StartIncrementalUpdate()) {
-        node->OnItemChanged(aItemId, aProperty, aIsAnnotationProperty,
-                            aNewValue, aLastModified, aItemType);
+        node->OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aValue);
       }
     }
   }
 
   // Note: we do NOT call history observers in this case. This notification is
   // the same as other history notification, except that here we know the item
   // is a bookmark. History observers will handle the history notification
   // instead.
@@ -4491,34 +4429,30 @@ nsNavHistoryResult::OnItemVisited(PRInt6
 // nsNavHistoryResult::OnItemMoved (nsINavBookmarkObserver)
 //
 //    Need to notify both the source and the destination folders (if they
 //    are different).
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnItemMoved(PRInt64 aItemId,
                                 PRInt64 aOldParent, PRInt32 aOldIndex,
-                                PRInt64 aNewParent, PRInt32 aNewIndex,
-                                PRUint16 aItemType)
+                                PRInt64 aNewParent, PRInt32 aNewIndex)
 {
   { // scope for loop index for VC6's broken for loop scoping
     ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aOldParent,
-        OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
-                    aItemType));
+        OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex));
   }
   if (aNewParent != aOldParent) {
     ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aNewParent,
-        OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
-                    aItemType));
+        OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex));
   }
   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
-                                                aNewParent, aNewIndex,
-                                                aItemType));
+                                                aNewParent, aNewIndex));
   ENUMERATE_HISTORY_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
-                                          aNewParent, aNewIndex, aItemType));
+                                          aNewParent, aNewIndex));
   return NS_OK;
 }
 
 // nsNavHistoryResult::OnVisit (nsINavHistoryObserver)
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,
                             PRInt64 aSessionId, PRInt64 aReferringId,
--- a/toolkit/components/places/src/nsNavHistoryResult.h
+++ b/toolkit/components/places/src/nsNavHistoryResult.h
@@ -46,16 +46,17 @@
 #define nsNavHistoryResult_h_
 
 #include "nsTArray.h"
 #include "nsInterfaceHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsNavHistory;
+class nsIWritablePropertyBag;
 class nsNavHistoryQuery;
 class nsNavHistoryQueryOptions;
 
 class nsNavHistoryContainerResultNode;
 class nsNavHistoryFolderResultNode;
 class nsNavHistoryQueryResultNode;
 class nsNavHistoryVisitResultNode;
 
@@ -101,16 +102,20 @@ private:
   NS_IMETHOD OnBeforeDeleteURI(nsIURI *aURI);                           \
   NS_IMETHOD OnDeleteURI(nsIURI *aURI);                                 \
   NS_IMETHOD OnClearHistory();                                          \
   NS_IMETHOD OnPageChanged(nsIURI *aURI, PRUint32 aWhat,                \
                            const nsAString &aValue);                    \
   NS_IMETHOD OnPageExpired(nsIURI* aURI, PRTime aVisitTime,             \
                            PRBool aWholeEntry);
 
+#define NS_DECL_EXTENDED_BOOKMARK_OBSERVER                              \
+  NS_IMETHOD OnItemAdded(PRInt64 aItemId, PRInt64 aFolder,              \
+                         PRInt32 aIndex, PRUint16 aItemType);
+
 // nsNavHistoryResult
 //
 //    nsNavHistory creates this object and fills in mChildren (by getting
 //    it through GetTopLevel()). Then FilledAllResults() is called to finish
 //    object initialization.
 //
 //    This object implements nsITreeView so you can just set it to a tree
 //    view and it will work. This object also observes the necessary history
@@ -131,16 +136,19 @@ public:
                                    nsNavHistoryContainerResultNode* aRoot,
                                    nsNavHistoryResult** result);
 
   // the tree viewer can go faster if it can bypass XPCOM
   friend class nsNavHistoryResultTreeViewer;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
 
+  nsresult PropertyBagFor(nsISupports* aObject,
+                          nsIWritablePropertyBag** aBag);
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSINAVHISTORYRESULT
   NS_DECL_BOOKMARK_HISTORY_OBSERVER
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsNavHistoryResult, nsINavHistoryResult)
 
   void AddHistoryObserver(nsNavHistoryQueryResultNode* aNode);
   void AddBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, PRInt64 aFolder);
   void AddAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
@@ -174,16 +182,19 @@ public:
   // This var states the fact we need to apply sortingMode in such a situation.
   PRBool mNeedsToApplySortingMode;
 
   // The sorting annotation to be used for in SORT_BY_ANNOTATION_* modes
   nsCString mSortingAnnotation;
 
   nsCOMPtr<nsINavHistoryResultViewer> mView;
 
+  // property bags for all result nodes, see PropertyBagFor
+  nsInterfaceHashtable<nsISupportsHashKey, nsIWritablePropertyBag> mPropertyBags;
+
   // node observers
   PRBool mIsHistoryObserver;
   PRBool mIsBookmarkFolderObserver;
   PRBool mIsAllBookmarksObserver;
 
   typedef nsTArray< nsRefPtr<nsNavHistoryQueryResultNode> > QueryObserverList;
   QueryObserverList mHistoryObservers;
   QueryObserverList mAllBookmarksObservers;
@@ -219,16 +230,20 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
   NS_IMETHOD GetTitle(nsACString& aTitle) \
     { aTitle = mTitle; return NS_OK; } \
   NS_IMETHOD GetAccessCount(PRUint32* aAccessCount) \
     { *aAccessCount = mAccessCount; return NS_OK; } \
   NS_IMETHOD GetTime(PRTime* aTime) \
     { *aTime = mTime; return NS_OK; } \
   NS_IMETHOD GetIndentLevel(PRInt32* aIndentLevel) \
     { *aIndentLevel = mIndentLevel; return NS_OK; } \
+  NS_IMETHOD GetViewIndex(PRInt32* aViewIndex) \
+    { *aViewIndex = mViewIndex; return NS_OK; } \
+  NS_IMETHOD SetViewIndex(PRInt32 aViewIndex) \
+    { mViewIndex = aViewIndex; return NS_OK; } \
   NS_IMETHOD GetBookmarkIndex(PRInt32* aIndex) \
     { *aIndex = mBookmarkIndex; return NS_OK; } \
   NS_IMETHOD GetDateAdded(PRTime* aDateAdded) \
     { *aDateAdded = mDateAdded; return NS_OK; } \
   NS_IMETHOD GetLastModified(PRTime* aLastModified) \
     { *aLastModified = mLastModified; return NS_OK; }
 
 #define NS_IMPLEMENT_SIMPLE_RESULTNODE \
@@ -242,24 +257,26 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 // implements all the simple getters instead of forwarding because they are so
 // short and we can save a virtual function call.
 //
 // (GetUri is redefined only by QueryResultNode and FolderResultNode because
 // the queries might not necessarily be parsed. The rest just return the node's
 // buffer.)
 #define NS_FORWARD_COMMON_RESULTNODE_TO_BASE_NO_GETITEMMID \
   NS_IMPLEMENT_SIMPLE_RESULTNODE_NO_GETITEMMID \
-  NS_IMETHOD GetIcon(nsACString& aIcon) \
+  NS_IMETHOD GetIcon(nsIURI** aIcon) \
     { return nsNavHistoryResultNode::GetIcon(aIcon); } \
   NS_IMETHOD GetParent(nsINavHistoryContainerResultNode** aParent) \
     { return nsNavHistoryResultNode::GetParent(aParent); } \
   NS_IMETHOD GetParentResult(nsINavHistoryResult** aResult) \
     { return nsNavHistoryResultNode::GetParentResult(aResult); } \
+  NS_IMETHOD GetPropertyBag(nsIWritablePropertyBag** aBag) \
+    { return nsNavHistoryResultNode::GetPropertyBag(aBag); } \
   NS_IMETHOD GetTags(nsAString& aTags) \
-    { return nsNavHistoryResultNode::GetTags(aTags); }
+    { return nsNavHistoryResultNode::GetTags(aTags); } \
 
 #define NS_FORWARD_COMMON_RESULTNODE_TO_BASE \
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE_NO_GETITEMMID \
   NS_IMETHOD GetItemId(PRInt64* aId) \
     { *aId = mItemId; return NS_OK; }
 
 class nsNavHistoryResultNode : public nsINavHistoryResultNode
 {
@@ -270,35 +287,34 @@ public:
   virtual ~nsNavHistoryResultNode() {}
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULTNODE_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsNavHistoryResultNode)
 
   NS_IMPLEMENT_SIMPLE_RESULTNODE
-  NS_IMETHOD GetIcon(nsACString& aIcon);
+  NS_IMETHOD GetIcon(nsIURI** aIcon);
   NS_IMETHOD GetParent(nsINavHistoryContainerResultNode** aParent);
   NS_IMETHOD GetParentResult(nsINavHistoryResult** aResult);
+  NS_IMETHOD GetPropertyBag(nsIWritablePropertyBag** aBag);
   NS_IMETHOD GetType(PRUint32* type)
     { *type = nsNavHistoryResultNode::RESULT_TYPE_URI; return NS_OK; }
   NS_IMETHOD GetUri(nsACString& aURI)
     { aURI = mURI; return NS_OK; }
   NS_IMETHOD GetTags(nsAString& aTags);
 
   virtual void OnRemoving();
 
   // Called from result's onItemChanged, see also bookmark observer declaration in
   // nsNavHistoryFolderResultNode
   NS_IMETHOD OnItemChanged(PRInt64 aItemId,
                            const nsACString &aProperty,
                            PRBool aIsAnnotationProperty,
-                           const nsACString &aValue,
-                           PRTime aNewLastModified,
-                           PRUint16 aItemType);
+                           const nsACString &aValue);
 
 public:
 
   nsNavHistoryResult* GetResult();
   nsNavHistoryQueryOptions* GetGeneratingOptions();
 
   // These functions test the type. We don't use a virtual function since that
   // would take a vtable slot for every one of (potentially very many) nodes.
@@ -387,16 +403,24 @@ public:
   PRInt32 mBookmarkIndex;
   PRInt64 mItemId;
   PRTime mDateAdded;
   PRTime mLastModified;
 
   // The indent level of this node. The root node will have a value of -1.  The
   // root's children will have a value of 0, and so on.
   PRInt32 mIndentLevel;
+
+  // Value used by the view for whatever it wants. For the built-in tree view,
+  // this is the index into the result's mVisibleElements list of this element.
+  // This is -1 if it is invalid. For items, >= 0 can be used to determine if
+  // the node is visible in the list or not. For folders, call IsVisible, since
+  // they can be the root node which is not itself visible, but its children
+  // are.
+  PRInt32 mViewIndex;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_IID)
 
 // nsNavHistoryVisitResultNode
 
 #define NS_IMPLEMENT_VISITRESULT \
   NS_IMETHOD GetUri(nsACString& aURI) { aURI = mURI; return NS_OK; } \
@@ -621,16 +645,18 @@ public:
   {
     nsCAutoString spec;
     if (NS_FAILED(aURI->GetSpec(spec)))
       return PR_FALSE;
     return FindChildURI(spec, aNodeIndex);
   }
   nsNavHistoryResultNode* FindChildURI(const nsACString& aSpec,
                                        PRUint32* aNodeIndex);
+  nsNavHistoryFolderResultNode* FindChildFolder(PRInt64 aFolderId,
+                                                PRUint32* aNodeIndex);
   nsNavHistoryContainerResultNode* FindChildContainerByName(const nsACString& aTitle,
                                                             PRUint32* aNodeIndex);
   // returns the index of the given node, -1 if not found
   PRInt32 FindChild(nsNavHistoryResultNode* aNode)
     { return mChildren.IndexOf(aNode); }
 
   nsresult InsertChildAt(nsNavHistoryResultNode* aNode, PRInt32 aIndex,
                          PRBool aIsTemporary = PR_FALSE);
@@ -643,17 +669,17 @@ public:
   nsresult RemoveChildAt(PRInt32 aIndex, PRBool aIsTemporary = PR_FALSE);
 
   void RecursiveFindURIs(PRBool aOnlyOne,
                          nsNavHistoryContainerResultNode* aContainer,
                          const nsCString& aSpec,
                          nsCOMArray<nsNavHistoryResultNode>* aMatches);
   void UpdateURIs(PRBool aRecursive, PRBool aOnlyOne, PRBool aUpdateSort,
                   const nsCString& aSpec,
-                  void (*aCallback)(nsNavHistoryResultNode*,void*, nsNavHistoryResult*),
+                  void (*aCallback)(nsNavHistoryResultNode*,void*),
                   void* aClosure);
   nsresult ChangeTitles(nsIURI* aURI, const nsACString& aNewTitle,
                         PRBool aRecursive, PRBool aOnlyOne);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryContainerResultNode,
                               NS_NAVHISTORYCONTAINERRESULTNODE_IID)
 
@@ -694,16 +720,17 @@ public:
   NS_DECL_NSINAVHISTORYQUERYRESULTNODE
 
   PRBool CanExpand();
   PRBool IsContainersQuery();
 
   virtual nsresult OpenContainer();
 
   NS_DECL_BOOKMARK_HISTORY_OBSERVER
+  NS_DECL_EXTENDED_BOOKMARK_OBSERVER
   virtual void OnRemoving();
 
 public:
   // this constructs lazily mURI from mQueries and mOptions, call
   // VerifyQueriesSerialized either this or mQueries/mOptions should be valid
   nsresult VerifyQueriesSerialized();
 
   // these may be constructed lazily from mURI, call VerifyQueriesParsed
@@ -767,16 +794,17 @@ public:
   NS_DECL_NSINAVHISTORYQUERYRESULTNODE
 
   virtual nsresult OpenContainer();
 
   // This object implements a bookmark observer interface without deriving from
   // the bookmark observers. This is called from the result's actual observer
   // and it knows all observers are FolderResultNodes
   NS_DECL_NSINAVBOOKMARKOBSERVER
+  NS_DECL_EXTENDED_BOOKMARK_OBSERVER
 
   virtual void OnRemoving();
 public:
 
   // this indicates whether the folder contents are valid, they don't go away
   // after the container is closed until a notification comes in
   PRBool mContentsValid;
 
--- a/toolkit/components/places/src/nsPlacesDBFlush.js
+++ b/toolkit/components/places/src/nsPlacesDBFlush.js
@@ -213,28 +213,27 @@ nsPlacesDBFlush.prototype = {
 
     // Restore our timer
     this._timer = this._newTimer();
 
     // We need to sync now
     this._flushWithQueries([kQuerySyncPlacesId, kQuerySyncHistoryVisitsId]);
   },
 
-  onItemAdded: function(aItemId, aParentId, aIndex, aItemType)
+  onItemAdded: function(aItemId, aParentId, aIndex)
   {
     // Sync only if we added a TYPE_BOOKMARK item.  Note, we want to run the
     // least amount of queries as possible here for performance reasons.
-    if (!this._inBatchMode && aItemType == this._bs.TYPE_BOOKMARK)
+    if (!this._inBatchMode &&
+        this._bs.getItemType(aItemId) == this._bs.TYPE_BOOKMARK)
       this._flushWithQueries([kQuerySyncPlacesId]);
   },
 
   onItemChanged: function DBFlush_onItemChanged(aItemId, aProperty,
-                                                aIsAnnotationProperty,
-                                                aNewValue, aLastModified,
-                                                aItemType)
+                                                aIsAnnotationProperty, aValue)
   {
     if (!this._inBatchMode && aProperty == "uri")
       this._flushWithQueries([kQuerySyncPlacesId]);
   },
 
   onBeforeItemRemoved: function() { },
   onItemRemoved: function() { },
   onItemVisited: function() { },
--- a/toolkit/components/places/src/nsTaggingService.js
+++ b/toolkit/components/places/src/nsTaggingService.js
@@ -220,17 +220,17 @@ TaggingService.prototype = {
     if (!result)
       return;
     var node = result.root;
     node.QueryInterface(Ci.nsINavHistoryContainerResultNode);
     node.containerOpen = true;
     var cc = node.childCount;
     node.containerOpen = false;
     if (cc == 0)
-      this._bms.removeItem(node.itemId);
+      this._bms.removeFolder(node.itemId);
   },
 
   // nsITaggingService
   untagURI: function TS_untagURI(aURI, aTags) {
     if (!aURI)
       throw Cr.NS_ERROR_INVALID_ARG;
 
     if (!aTags) {
@@ -392,31 +392,56 @@ TaggingService.prototype = {
   // nsINavBookmarkObserver
   onBeginUpdateBatch: function() {
     this._inBatch = true;
   },
   onEndUpdateBatch: function() {
     this._inBatch = false;
   },
 
-  onItemAdded: function(aItemId, aFolderId, aIndex, aItemType) {
+  onItemAdded: function(aItemId, aFolderId, aIndex) {
     // Nothing to do if this is not a tag.
-    if (aFolderId != this._bms.tagsFolder ||
-        aItemType != this._bms.TYPE_FOLDER)
+    if (aFolderId != this._bms.tagsFolder)
       return;
 
-    this._tagFolders[aItemId] = this._bms.getItemTitle(aItemId);
+    // If we are correctly called through createTag the itemId will be added
+    // to _tagFolders just after onItemAdded is called.  To avoid an useless
+    // call to getItemType we enqueue this check, so that when it runs the hash
+    // has already been updated.
+    // TODO: once bug 494380 is fixed, this 'workaround' can go away.
+    var self = this;
+    var tm = Cc["@mozilla.org/thread-manager;1"].
+             getService(Ci.nsIThreadManager);
+    tm.mainThread.dispatch({
+      run: function() {
+        try {
+          if (!self._tagFolders[aItemId] &&
+              self._bms.getItemType(aItemId) == self._bms.TYPE_FOLDER)
+            self._tagFolders[aItemId] = self._bms.getItemTitle(aItemId);
+        }
+        catch(ex) {
+          // Could happen that the tag is removed just after it is added, for
+          // example with transactions.  in such a case getting item type
+          // will fail and there's no reason to register the addition.
+        }
+      }
+    }, Ci.nsIThread.DISPATCH_NORMAL);
   },
 
-  onBeforeItemRemoved: function(aItemId, aItemType) {
-    if (aItemType == this._bms.TYPE_BOOKMARK)
+  onBeforeItemRemoved: function(aItemId) {
+    // Remember the bookmark's URI, because it will be gone by the time
+    // onItemRemoved() is called.  getBookmarkURI() will throw if the item is
+    // not a bookmark, which is fine.
+    try {
       this._itemsInRemoval[aItemId] = this._bms.getBookmarkURI(aItemId);
+    }
+    catch (e) {}
   },
 
-  onItemRemoved: function(aItemId, aFolderId, aIndex, aItemType) {
+  onItemRemoved: function(aItemId, aFolderId, aIndex) {
     var itemURI = this._itemsInRemoval[aItemId];
     delete this._itemsInRemoval[aItemId];
 
     // Item is a tag folder.
     if (aFolderId == this._bms.tagsFolder && this._tagFolders[aItemId])
       delete this._tagFolders[aItemId];
 
     // Item is a bookmark that was removed from a non-tag folder.
@@ -426,26 +451,24 @@ TaggingService.prototype = {
       // contained in tag folders, the URI is no longer properly bookmarked, so
       // untag it.
       var tagIds = this._getTagsIfUnbookmarkedURI(itemURI);
       if (tagIds)
         this.untagURI(itemURI, tagIds);
     }
   },
 
-  onItemChanged: function(aItemId, aProperty, aIsAnnotationProperty, aNewValue,
-                          aLastModified, aItemType) {
+  onItemChanged: function(aItemId, aProperty, aIsAnnotationProperty, aValue) {
     if (aProperty == "title" && this._tagFolders[aItemId])
       this._tagFolders[aItemId] = this._bms.getItemTitle(aItemId);
   },
 
   onItemVisited: function(aItemId, aVisitID, time) {},
 
-  onItemMoved: function(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
-                        aItemType) {
+  onItemMoved: function(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex) {
     if (this._tagFolders[aItemId] && this._bms.tagFolder == aOldParent &&
         this._bms.tagFolder != aNewParent)
       delete this._tagFolders[aItemId];
   }
 };
 
 // Implements nsIAutoCompleteResult
 function TagAutoCompleteResult(searchString, searchResult,
--- a/toolkit/components/places/tests/bookmarks/test_393498.js
+++ b/toolkit/components/places/tests/bookmarks/test_393498.js
@@ -42,33 +42,32 @@ try {
 } catch(ex) {
   do_throw("Could not get services\n");
 }
 
 // create and add bookmarks observer
 var observer = {
   onBeginUpdateBatch: function() {},
   onEndUpdateBatch: function() {},
-  onItemAdded: function(id, folder, index, itemType) {
+  onItemAdded: function(id, folder, index) {
     this._itemAddedId = id;
     this._itemAddedParent = folder;
     this._itemAddedIndex = index;
   },
-  onBeforeItemRemoved: function() {},
-  onItemRemoved: function() {},
+  onBeforeItemRemoved: function(id) {},
+  onItemRemoved: function(id, folder, index) {},
   _itemChangedProperty: null,
-  onItemChanged: function(id, property, isAnnotationProperty, value,
-                          lastModified, itemType) {
+  onItemChanged: function(id, property, isAnnotationProperty, value) {
     this._itemChangedId = id;
     this._itemChangedProperty = property;
     this._itemChanged_isAnnotationProperty = isAnnotationProperty;
     this._itemChangedValue = value;
   },
-  onItemVisited: function() {},
-  onItemMoved: function() {},
+  onItemVisited: function(id, visitID, time) {},
+  onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex) {},
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsINavBookmarkObserver) ||
         iid.equals(Ci.nsISupports)) {
       return this;
     }
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 };
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks.js
@@ -61,41 +61,40 @@ try {
 // create and add bookmarks observer
 var observer = {
   onBeginUpdateBatch: function() {
     this._beginUpdateBatch = true;
   },
   onEndUpdateBatch: function() {
     this._endUpdateBatch = true;
   },
-  onItemAdded: function(id, folder, index, itemType) {
+  onItemAdded: function(id, folder, index) {
     this._itemAddedId = id;
     this._itemAddedParent = folder;
     this._itemAddedIndex = index;
   },
-  onBeforeItemRemoved: function(){},
-  onItemRemoved: function(id, folder, index, itemType) {
+  onBeforeItemRemoved: function(id) {
+  },
+  onItemRemoved: function(id, folder, index) {
     this._itemRemovedId = id;
     this._itemRemovedFolder = folder;
     this._itemRemovedIndex = index;
   },
-  onItemChanged: function(id, property, isAnnotationProperty, value,
-                          lastModified, itemType) {
+  onItemChanged: function(id, property, isAnnotationProperty, value) {
     this._itemChangedId = id;
     this._itemChangedProperty = property;
     this._itemChanged_isAnnotationProperty = isAnnotationProperty;
     this._itemChangedValue = value;
   },
   onItemVisited: function(id, visitID, time) {
     this._itemVisitedId = id;
     this._itemVisitedVistId = visitID;
     this._itemVisitedTime = time;
   },
-  onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex,
-                        itemType) {
+  onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex) {
     this._itemMovedId = id
     this._itemMovedOldParent = oldParent;
     this._itemMovedOldIndex = oldIndex;
     this._itemMovedNewParent = newParent;
     this._itemMovedNewIndex = newIndex;
   },
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsINavBookmarkObserver) ||
@@ -336,16 +335,37 @@ function run_test() {
   // move item, appending, to different folder
   bmsvc.moveItem(newId5, testRoot, bmsvc.DEFAULT_INDEX);
   do_check_eq(observer._itemMovedId, newId5);
   do_check_eq(observer._itemMovedOldParent, homeFolder);
   do_check_eq(observer._itemMovedOldIndex, 0);
   do_check_eq(observer._itemMovedNewParent, testRoot);
   do_check_eq(observer._itemMovedNewIndex, 3);
 
+  // test insertSeparator and removeChildAt
+  // XXX - this should also query bookmarks for the folder children
+  // and then test the node type at our index
+  try {
+    bmsvc.insertSeparator(testRoot, 1);
+    bmsvc.removeChildAt(testRoot, 1);
+  } catch(ex) {
+    do_throw("insertSeparator: " + ex);
+  }
+
+  // XXX test getItemType for separators 
+  // add when 379952 is fixed
+
+  // removeChildAt w/ folder
+  bmsvc.createFolder(testRoot, "tmp", 1);
+  bmsvc.removeChildAt(testRoot, 1);
+
+  // removeChildAt w/ bookmark
+  bmsvc.insertBookmark(root, uri("http://blah.com"), 1, "");
+  bmsvc.removeChildAt(root, 1);
+
   // test get folder's index 
   var tmpFolder = bmsvc.createFolder(testRoot, "tmp", 2);
   do_check_eq(bmsvc.getItemIndex(tmpFolder), 2);
 
   // test setKeywordForBookmark
   var kwTestItemId = bmsvc.insertBookmark(testRoot, uri("http://keywordtest.com"),
                                           bmsvc.DEFAULT_INDEX, "");
   try {
--- a/toolkit/components/places/tests/bookmarks/test_onBeforeItemRemoved_observer.js
+++ b/toolkit/components/places/tests/bookmarks/test_onBeforeItemRemoved_observer.js
@@ -52,38 +52,35 @@ let bs = Cc["@mozilla.org/browser/nav-bo
 //// Observer
 
 function Observer(aExpectedId)
 {
 }
 Observer.prototype =
 {
   checked: false,
-  onItemMovedCalled: false,
-  onItemRemovedCalled: false,
   onBeginUpdateBatch: function() {
   },
   onEndUpdateBatch: function() {
   },
   onItemAdded: function(id, folder, index) {
   },
   onBeforeItemRemoved: function(id) {
     this.removedId = id;
   },
   onItemRemoved: function(id, folder, index) {
     do_check_false(this.checked);
     do_check_eq(this.removedId, id);
-    this.onItemRemovedCalled = true;
+    this.checked = true;
   },
   onItemChanged: function(id, property, isAnnotationProperty, value) {
   },
   onItemVisited: function(id, visitID, time) {
   },
   onItemMoved: function(id, oldParent, oldIndex, newParent, newIndex) {
-    this.onItemMovedCalled = true;
   },
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsINavBookmarkObserver) ||
         iid.equals(Ci.nsISupports)) {
       return this;
     }
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
@@ -99,49 +96,49 @@ function test_removeItem()
                              uri("http://mozilla.org"), bs.DEFAULT_INDEX, "t");
 
   // Add our observer, and remove it.
   let observer = new Observer(id);
   bs.addObserver(observer, false);
   bs.removeItem(id);
 
   // Make sure we were notified!
-  do_check_true(observer.onItemRemovedCalled);
+  do_check_true(observer.checked);
   bs.removeObserver(observer);
 }
 
 function test_removeFolder()
 {
   // First we add the item we are going to remove.
   let id = bs.createFolder(bs.unfiledBookmarksFolder, "t", bs.DEFAULT_INDEX);
 
   // Add our observer, and remove it.
   let observer = new Observer(id);
   bs.addObserver(observer, false);
   bs.removeItem(id);
 
   // Make sure we were notified!
-  do_check_true(observer.onItemRemovedCalled);
+  do_check_true(observer.checked);
   bs.removeObserver(observer);
 }
 
 function test_removeFolderChildren()
 {
   // First we add the item we are going to remove.
   let fid = bs.createFolder(bs.unfiledBookmarksFolder, "tf", bs.DEFAULT_INDEX);
   let id = bs.insertBookmark(fid, uri("http://mozilla.org"), bs.DEFAULT_INDEX,
                              "t");
 
   // Add our observer, and remove it.
   let observer = new Observer(id);
   bs.addObserver(observer, false);
   bs.removeFolderChildren(fid);
 
   // Make sure we were notified!
-  do_check_true(observer.onItemRemovedCalled);
+  do_check_true(observer.checked);
   bs.removeObserver(observer);
 }
 
 function test_setItemIndex()
 {
   // First we add the item we are going to move, and one additional one.
   let id = bs.insertBookmark(bs.unfiledBookmarksFolder,
                              uri("http://mozilla.org/1"), 0, "t1");
@@ -149,17 +146,17 @@ function test_setItemIndex()
                     "t1");
 
   // Add our observer, and move it.
   let observer = new Observer(id);
   bs.addObserver(observer, false);
   bs.setItemIndex(id, 2);
 
   // Make sure we were notified!
-  do_check_true(observer.onItemMovedCalled);
+  do_check_true(observer.checked);
   bs.removeObserver(observer);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Runner
 
 let tests = [
   test_removeItem,
--- a/toolkit/components/places/tests/bookmarks/test_removeItem.js
+++ b/toolkit/components/places/tests/bookmarks/test_removeItem.js
@@ -50,14 +50,21 @@ function run_test() {
 
   // add a bookmark to the new folder
   var bookmarkURI = uri("http://iasdjkf");
   do_check_false(PlacesUtils.bookmarks.isBookmarked(bookmarkURI));
   var bookmarkId = PlacesUtils.bookmarks.insertBookmark(folderId, bookmarkURI,
                                                         DEFAULT_INDEX, "");
   do_check_eq(PlacesUtils.bookmarks.getItemTitle(bookmarkId), "");
 
+  // try to remove the bookmark using removeFolder
+  try {
+    PlacesUtils.bookmarks.removeFolder(bookmarkId);
+    do_throw("no exception when removing a bookmark via removeFolder()!");
+  } catch(ex) {}
+  do_check_true(PlacesUtils.bookmarks.isBookmarked(bookmarkURI));
+
   // remove the folder using removeItem
   PlacesUtils.bookmarks.removeItem(folderId);
   do_check_eq(PlacesUtils.bookmarks.getBookmarkIdsForURI(bookmarkURI, {}).length, 0);
   do_check_false(PlacesUtils.bookmarks.isBookmarked(bookmarkURI));
   do_check_eq(PlacesUtils.bookmarks.getItemIndex(bookmarkId), -1);
 }
--- a/toolkit/components/places/tests/chrome/test_329534.xul
+++ b/toolkit/components/places/tests/chrome/test_329534.xul
@@ -64,34 +64,34 @@ var observer =
         iid.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   // nsINavBookmarkObserver
   onBeginUpdateBatch: function(){},
   onEndUpdateBatch: function(){},
-  onItemAdded: function(bookmarkId, folderId, index, itemType) {
+  onItemAdded: function(bookmarkId, folderId, index) {
     if ( status == 0 ) {
       runTest1( folderId );
     } else {
       runTest2( folderId );
       if ( status == 2 ) {
         bmsvc.removeObserver(this);
-        bmsvc.removeItem(gLivemarkId1);
-        bmsvc.removeItem(gLivemarkId2);
+        bmsvc.removeFolder(gLivemarkId1);
+        bmsvc.removeFolder(gLivemarkId2);
         SimpleTest.finish();
       }
     }
   },
-  onBeforeItemRemoved: function(){},
-  onItemRemoved: function() {},
-  onItemChanged: function() {}
-  onItemVisited: function() {}
-  onItemMoved: function() {}
+  onBeforeItemRemoved: function(bookmarkId){},
+  onItemRemoved: function(bookmarkId, bookmark, folder, index){},
+  onItemChanged: function(bookmarkId, property, isAnnotationProperty, value){},
+  onItemVisited: function(bookmarkId, bookmark, aVisitID, time){},
+  onItemMoved: function(itemId, oldParent, oldIndex, newParent, newIndex){}
 };
 bmsvc.addObserver(observer, false);
 
 // start updating livemarks
 firstLoadTime = Date.now();
 lmsvc.start();
 
 // First
--- a/toolkit/components/places/tests/chrome/test_341972a.xul
+++ b/toolkit/components/places/tests/chrome/test_341972a.xul
@@ -52,17 +52,17 @@ var observer =
   // nsIAnnotationObserver
   onItemAnnotationSet: function(aItemId, aAnnotationName) {
     if (aAnnotationName == "livemark/siteURI" &&
         aItemId == gLivemarkId) {
       var newSiteURI = annosvc.getItemAnnotation(aItemId, aAnnotationName);
       ok(newSiteURI == FEEDSITESPEC,
          "livemark site URI changed to " + newSiteURI + " not to value in feed");
       annosvc.removeObserver(this);
-      bmsvc.removeItem(gLivemarkId);
+      bmsvc.removeFolder(gLivemarkId);
       SimpleTest.finish();
     }
   },
   onItemAnnotationRemoved: function(aItemId, aAnnotationName) {},
   onPageAnnotationSet: function(aUri, aAnnotationName) {},
   onPageAnnotationRemoved: function(aUri, aAnnotationName) {}
 };
 
--- a/toolkit/components/places/tests/chrome/test_342484.xul
+++ b/toolkit/components/places/tests/chrome/test_342484.xul
@@ -48,22 +48,22 @@ var observer =
   },
 
   // nsINavBookmarkObserve
   onBeginUpdateBatch: function(){},
   onEndUpdateBatch: function(){
     setTimeout(runTest, 1000);
     bmsvc.removeObserver(this);
   },
-  onItemAdded: function(){}.
-  onBeforeItemRemoved: function(){},
-  onItemRemoved: function(){},
-  onItemChanged: function(){},
-  onItemVisited: function(){},
-  onItemMoved: function(){},
+  onItemAdded: function(itemId, folder, index) {},
+  onBeforeItemRemoved: function(itemId){},
+  onItemRemoved: function(itemId, folder, index){},
+  onItemChanged: function(itemId, property, isAnnotationProperty, value){},
+  onItemVisited: function(itemId, aVisitID, time){},
+  onItemMoved: function(itemId, oldParent, oldIndex, newParent, newIndex){},
 
 };
 
 function runTest() {
   var goodItems = ["http://example.org/first", "http://example.org/last"];
   var options = histsvc.getNewQueryOptions();
   var query = histsvc.getNewQuery();
   query.setFolders([gLivemarkId], 1);
@@ -74,17 +74,17 @@ function runTest() {
   var cc = rootNode.childCount;
   ok(cc == 2, "failed to create the two good livemark items");
   for (var i = 0; i < cc; ++i) {
     var node = rootNode.getChild(i);
     ok(goodItems.indexOf(node.uri) != -1, "livemark item created with bad uri " + node.uri);
   }
 
   rootNode.containerOpen = false;
-  bmsvc.removeItem(gLivemarkId);
+  bmsvc.removeFolder(gLivemarkId);
   SimpleTest.finish();
 }
 
 const FEEDSPEC = "http://localhost:8888/tests/toolkit/components/places/tests/chrome/bad_links.atom";
 
 gLivemarkId = lmsvc.createLivemarkFolderOnly(toolbarFolderId, "foo",
                                              uri("http:/localhost/"),
                                              uri(FEEDSPEC), -1);
--- a/toolkit/components/places/tests/chrome/test_371798.xul
+++ b/toolkit/components/places/tests/chrome/test_371798.xul
@@ -54,26 +54,25 @@ var observer =
         iid.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   // nsINavBookmarkObserver
   onBeginUpdateBatch: function(){},
   onEndUpdateBatch: function(){},
-  onItemAdded: function(){},
-  onBeforeItemRemoved: function(){},
-  onItemRemoved: function(){},
-  onItemChanged: function(bookmarkId, property, isAnnotationProperty, value,
-                          lastModified, itemType){
+  onItemAdded: function(bookmarkId, bookmark, folder, index) {},
+  onBeforeItemRemoved: function(bookmarkId){},
+  onItemRemoved: function(bookmarkId, bookmark, folder, index){},
+  onItemChanged: function(bookmarkId, property, isAnnotationProperty, value){
     runTest();
     bmsvc.removeObserver(this);
   },
-  onItemVisited: function(){},
-  onItemMoved: function(){}
+  onItemVisited: function(bookmarkId, bookmark, aVisitID, time){},
+  onItemMoved: function(itemId, oldParent, oldIndex, newParent, newIndex){}
 };
 bmsvc.addObserver(observer, false);
 
 // modify the bookmark's title
 var newTitle = "foo";
 bmsvc.setItemTitle(bm2, newTitle);
 
 /*
--- a/toolkit/components/places/tests/chrome/test_381357.xul
+++ b/toolkit/components/places/tests/chrome/test_381357.xul
@@ -46,29 +46,29 @@ var observer =
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   // nsINavBookmarkObserve
   onBeginUpdateBatch: function(){},
   onEndUpdateBatch: function(){
   },
-  onItemAdded: function(itemId, folder, index, itemType) {
+  onItemAdded: function(itemId, folder, index) {
     var title = bmsvc.getItemTitle(itemId);
     if (title == "The First Title") {
       ok(true, "Item with title loaded");
       bmsvc.removeObserver(this);
       SimpleTest.finish();
     }
   },
-  onBeforeItemRemoved: function(){},
-  onItemRemoved: function(){},
-  onItemChanged: function(){},
-  onItemVisited: function(){},
-  onItemMoved: function(){},
+  onBeforeItemRemoved: function(itemId){},
+  onItemRemoved: function(itemId, folder, index){},
+  onItemChanged: function(itemId, property, isAnnotationProperty, value){},
+  onItemVisited: function(itemId, aVisitID, time){},
+  onItemMoved: function(itemId, oldParent, oldIndex, newParent, newIndex){},
 
 };
 
 const FEEDSPEC = "http://localhost:8888/tests/toolkit/components/places/tests/chrome/rss_as_html.rss";
 
 gLivemarkId = lmsvc.createLivemarkFolderOnly(toolbarFolderId, "foo",
                                              uri("http:/localhost/"),
                                              uri(FEEDSPEC), -1);
--- a/toolkit/components/places/tests/queries/test_abstime-annotation-domain.js
+++ b/toolkit/components/places/tests/queries/test_abstime-annotation-domain.js
@@ -171,16 +171,17 @@ function run_test() {
 
   // Options
   var options = histsvc.getNewQueryOptions();
   options.sortingMode = options.SORT_BY_URI_ASCENDING;
   options.resultType = options.RESULTS_AS_URI;
   // The next two options should be ignored
   // can't use this one, breaks test - bug 419779
   // options.excludeItems = true;
+  options.showSessions = true;
 
   // Results
   var result = histsvc.executeQuery(query, options);
   var root = result.root;
   root.containerOpen = true;
 
   // Ensure the result set is correct
   compareArrayToResult(testData, root);
--- a/toolkit/components/places/tests/queries/test_abstime-annotation-uri.js
+++ b/toolkit/components/places/tests/queries/test_abstime-annotation-uri.js
@@ -169,16 +169,17 @@ function run_test() {
 
   // Options
   var options = histsvc.getNewQueryOptions();
   options.sortingMode = options.SORT_BY_URI_ASCENDING;
   options.resultType = options.RESULTS_AS_URI;
   // The next two options should be ignored
   // can't use this one, breaks test - bug 419779
   // options.excludeItems = true;
+  options.showSessions = true;
 
   // Results
   var result = histsvc.executeQuery(query, options);
   var root = result.root;
   root.containerOpen = true;
 
   // Ensure the result set is correct
   compareArrayToResult(testData, root);
--- a/toolkit/components/places/tests/queries/test_querySerialization.js
+++ b/toolkit/components/places/tests/queries/test_querySerialization.js
@@ -458,16 +458,27 @@ const queryOptionSwitches = [
     desc:     "nsINavHistoryQueryOptions.includeHidden",
     matches:  simplePropertyMatches,
     runs:     [
       function (aQuery, aQueryOptions) {
         aQueryOptions.includeHidden = true;
       }
     ]
   },
+  // showSessions
+  {
+    property: "showSessions",
+    desc:     "nsINavHistoryQueryOptions.showSessions",
+    matches:  simplePropertyMatches,
+    runs:     [
+      function (aQuery, aQueryOptions) {
+        aQueryOptions.showSessions = true;
+      }
+    ]
+  },
   // maxResults
   {
     property: "maxResults",
     desc:     "nsINavHistoryQueryOptions.maxResults",
     matches:  simplePropertyMatches,
     runs:     [
       function (aQuery, aQueryOptions) {
         aQueryOptions.maxResults = 0xffffffff; // 2^32 - 1
--- a/toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
@@ -50,21 +50,20 @@ const TEST_URI = "http://test.com/";
 const MODIFIED_URI = "http://test.com/index.html";
 
 const SYNC_INTERVAL = 600; // ten minutes
 const kSyncPrefName = "syncDBTableIntervalInSecs";
 const kSyncFinished = "places-sync-finished";
 
 // Used to update observer itemId
 var bookmarksObserver = {
-  onItemAdded: function(aItemId, aNewParent, aNewIndex, aItemType) {
+  onItemAdded: function(aItemId, aNewParent, aNewIndex) {
     observer.itemId = aItemId;
   },
-  onItemChanged: function(aItemId, aProperty, aNewValue, aLastModified,
-                          aItemType) {
+  onItemChanged: function(aItemId, aProperty, aValue) {
     if (aProperty == "uri")
       do_check_eq(observer.itemId, aItemId);
   }
 }
 bs.addObserver(bookmarksObserver, false);
 
 var observer = {
   itemId: -1,
--- a/toolkit/components/places/tests/unit/nsDummyObserver.js
+++ b/toolkit/components/places/tests/unit/nsDummyObserver.js
@@ -59,22 +59,22 @@ DummyObserver.prototype = {
   onDeleteURI: function(aURI) {},
   onClearHistory: function() {},
   onPageChanged: function(aURI, aWhat, aValue) {},
   onPageExpired: function(aURI, aVisitTime, aWholeEntry) {},
 
   // bookmark observer
   //onBeginUpdateBatch: function() {},
   //onEndUpdateBatch: function() {},
-  onItemAdded: function(aItemId, aParentId, aIndex, aItemType) {
+  onItemAdded: function(aItemId, aParentId, aIndex) {
     let os = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
     os.notifyObservers(null, "dummy-observer-item-added", null);
   },
-  onItemChanged: function () {},
+  onItemChanged: function (aItemId, aProperty, aIsAnnotationProperty, aValue) {},
   onBeforeItemRemoved: function() {},
   onItemRemoved: function() {},
   onItemVisited: function() {},
   onItemMoved: function() {},
 
   classDescription: "Dummy observer used to test category observers",
   classID: Components.ID("62e221d3-68c3-4e1a-8943-a27beb5005fe"),
   contractID: "@mozilla.org/places/test/dummy-observer;1",
--- a/toolkit/components/places/tests/unit/test_457698_crash.js
+++ b/toolkit/components/places/tests/unit/test_457698_crash.js
@@ -46,17 +46,17 @@
 } catch (ex) {
   do_throw("Could not get services\n");
 }
 
 // create and add bookmarks observer
 var observer = {
   onBeginUpdateBatch: function() {},
   onEndUpdateBatch: function() {},
-  onItemAdded: function(id, folder, index, itemType) {
+  onItemAdded: function(id, folder, index) {
     do_check_true(id > 0);
   },
   onBeforeItemRemoved: function() {},
   onItemRemoved: function() {},
   onItemChanged: function() {},
   onItemVisited: function() {},
   onItemMoved: function() {},
   QueryInterface: function(iid) {
--- a/toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
+++ b/toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
@@ -60,81 +60,72 @@ function add_visit(aURI, aDate) {
                                  histsvc.TRANSITION_TYPED, // user typed in URL bar
                                  false, // not redirect
                                  0);
   do_check_true(placeID > 0);
   return placeID;
 }
 
 var viewer = {
-  insertedNode: null,
-  nodeInserted: function(parent, node, newIndex) {
-    this.insertedNode = node;
+  insertedItem: null,
+  itemInserted: function(parent, item, newIndex) {
+    this.insertedItem = item;
   },
-  removedNode: null,
-  nodeRemoved: function(parent, node, oldIndex) {
-    this.removedNode = node;
-  },
-
-  newTitle: "",
-  nodeChangedByTitle: null,
-  nodeTitleChanged: function(node, newTitle) {
-    this.nodeChangedByTitle = node;
-    this.newTitle = newTitle;
+  removedItem: null,
+  itemRemoved: function(parent, item, oldIndex) {
+    this.removedItem = item;
   },
-
-  newAccessCount: 0,
-  newTime: 0,
-  nodeChangedByHistoryDetails: null,
-  nodeHistoryDetailsChanged: function(node,
-                                         updatedVisitDate,
-                                         updatedVisitCount) {
-    this.nodeChangedByHistoryDetails = node
-    this.newTime = updatedVisitDate;
-    this.newAccessCount = updatedVisitCount;
+  changedItem: null,
+  itemChanged: function(item) {
+    this.changedItem = item;
   },
-
-  replacedNode: null,
-  nodeReplaced: function(parent, oldNode, newNode, index) {
-    this.replacedNode = node;
+  replacedItem: null,
+  itemReplaced: function(parent, oldItem, newItem, index) {
+    dump("itemReplaced: " + newItem.uri + "\n");
+    this.replacedItem = item;
   },
-  movedNode: null,
-  nodeMoved: function(node, oldParent, oldIndex, newParent, newIndex) {
-    this.movedNode = node;
+  movedItem: null,
+  itemMoved: function(item, oldParent, oldIndex, newParent, newIndex) {
+    this.movedItem = item;
   },
   openedContainer: null,
-  containerOpened: function(node) {
-    this.openedContainer = node;
+  containerOpened: function(item) {
+    this.openedContainer = item;
   },
   closedContainer: null,
-  containerClosed: function(node) {
-    this.closedContainer = node;
+  containerClosed: function(item) {
+    this.closedContainer = item;
   },
   invalidatedContainer: null,
-  invalidateContainer: function(node) {    
-    this.invalidatedContainer = node;
+  invalidateContainer: function(item) {
+    dump("invalidateContainer()\n");
+    this.invalidatedContainer = item;
+  },
+  allInvalidated: null,
+  invalidateAll: function() {
+    this.allInvalidated = true;
   },
   sortingMode: null,
   sortingChanged: function(sortingMode) {
     this.sortingMode = sortingMode;
   },
   result: null,
   ignoreInvalidateContainer: false,
   addViewObserver: function(observer, ownsWeak) {},
   removeViewObserver: function(observer) {},
   reset: function() {
-    this.insertedNode = null;
-    this.removedNode = null;
-    this.nodeChangedByTitle = null;
-    this.nodeChangedByHistoryDetails = null;
-    this.replacedNode = null;
-    this.movedNode = null;
+    this.insertedItem = null;
+    this.removedItem = null;
+    this.changedItem = null;
+    this.replacedItem = null;
+    this.movedItem = null;
     this.openedContainer = null;
     this.closedContainer = null;
     this.invalidatedContainer = null;
+    this.allInvalidated = null;
     this.sortingMode = null;
   }
 };
 
 // main
 function run_test() {
 
   // history query
@@ -145,48 +136,48 @@ function run_test() {
   var result = histsvc.executeQuery(query, options);
   result.viewer = viewer;
   var root = result.root;
   root.containerOpen = true;
 
   // nsINavHistoryResultViewer.containerOpened
   do_check_neq(viewer.openedContainer, null);
 
-  // nsINavHistoryResultViewer.nodeInserted
+  // nsINavHistoryResultViewer.itemInserted
   // add a visit
   var testURI = uri("http://mozilla.com");
   add_visit(testURI);
-  do_check_eq(testURI.spec, viewer.insertedNode.uri);
+  do_check_eq(testURI.spec, viewer.insertedItem.uri);
 
-  // nsINavHistoryResultViewer.nodeHistoryDetailsChanged
-  // adding a visit causes nodeHistoryDetailsChanged for the folder
-  do_check_eq(root.uri, viewer.nodeChangedByHistoryDetails.uri);
+  // nsINavHistoryResultViewer.itemChanged
+  // adding a visit causes itemChanged for the folder
+  do_check_eq(root.uri, viewer.changedItem.uri);
 
-  // nsINavHistoryResultViewer.itemTitleChanged for a leaf node
+  // nsINavHistoryResultViewer.itemChanged for a leaf node
   bhist.addPageWithDetails(testURI, "baz", Date.now() * 1000);
-  do_check_eq(viewer.nodeChangedByTitle.title, "baz");
+  do_check_eq(viewer.changedItem.title, "baz");
 
-  // nsINavHistoryResultViewer.nodeRemoved
+  // nsINavHistoryResultViewer.itemRemoved
   var removedURI = uri("http://google.com");
   add_visit(removedURI);
   bhist.removePage(removedURI);
-  do_check_eq(removedURI.spec, viewer.removedNode.uri);
+  do_check_eq(removedURI.spec, viewer.removedItem.uri);
 
-  // XXX nsINavHistoryResultViewer.nodeReplaced
-  // NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.NodeReplaced()
+  // XXX nsINavHistoryResultViewer.itemReplaced
+  // NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.ItemReplaced()
 
   // nsINavHistoryResultViewer.invalidateContainer
   bhist.removePagesFromHost("mozilla.com", false);
   do_check_eq(root.uri, viewer.invalidatedContainer.uri);
 
+  // nsINavHistoryResultViewer.invalidateAll
   // nsINavHistoryResultViewer.sortingChanged
-  viewer.invalidatedContainer = null;
   result.sortingMode = options.SORT_BY_TITLE_ASCENDING;
+  do_check_true(viewer.allInvalidated);
   do_check_eq(viewer.sortingMode, options.SORT_BY_TITLE_ASCENDING);
-  do_check_eq(viewer.invalidatedContainer, result.root);
 
   // nsINavHistoryResultViewer.containerClosed
   root.containerOpen = false;
   do_check_eq(viewer.closedContainer, viewer.openedContainer);
   result.viewer = null;
 
   // bookmarks query
   
@@ -205,47 +196,46 @@ function run_test() {
   var result = histsvc.executeQuery(query, options);
   result.viewer = viewer;
   var root = result.root;
   root.containerOpen = true;
 
   // nsINavHistoryResultViewer.containerOpened
   do_check_neq(viewer.openedContainer, null);
 
-  // nsINavHistoryResultViewer.nodeInserted
+  // nsINavHistoryResultViewer.itemInserted
   // add a bookmark
   var testBookmark = bmsvc.insertBookmark(bmsvc.bookmarksMenuFolder, testURI, bmsvc.DEFAULT_INDEX, "foo");
-  do_check_eq("foo", viewer.insertedNode.title);
-  do_check_eq(testURI.spec, viewer.insertedNode.uri);
+  do_check_eq("foo", viewer.insertedItem.title);
+  do_check_eq(testURI.spec, viewer.insertedItem.uri);
 
-  // nsINavHistoryResultViewer.nodeHistoryDetailsChanged
-  // adding a visit causes nodeHistoryDetailsChanged for the folder
-  do_check_eq(root.uri, viewer.nodeChangedByHistoryDetails.uri);
+  // nsINavHistoryResultViewer.itemChanged
+  // adding a visit causes itemChanged for the folder
+  do_check_eq(root.uri, viewer.changedItem.uri);
 
-  // nsINavHistoryResultViewer.nodeTitleChanged for a leaf node
+  // nsINavHistoryResultViewer.itemChanged for a leaf node
   bmsvc.setItemTitle(testBookmark, "baz");
-  do_check_eq(viewer.nodeChangedByTitle.title, "baz");
-  do_check_eq(viewer.newTitle, "baz");
+  do_check_eq(viewer.changedItem.title, "baz");
 
   var testBookmark2 = bmsvc.insertBookmark(bmsvc.bookmarksMenuFolder, uri("http://google.com"), bmsvc.DEFAULT_INDEX, "foo");
   bmsvc.moveItem(testBookmark2, bmsvc.bookmarksMenuFolder, 0);
-  do_check_eq(viewer.movedNode.itemId, testBookmark2);
+  do_check_eq(viewer.movedItem.itemId, testBookmark2);
 
-  // nsINavHistoryResultViewer.nodeRemoved
+  // nsINavHistoryResultViewer.itemRemoved
   bmsvc.removeItem(testBookmark2);
-  do_check_eq(testBookmark2, viewer.removedNode.itemId);
+  do_check_eq(testBookmark2, viewer.removedItem.itemId);
 
-  // XXX nsINavHistoryResultViewer.nodeReplaced
-  // NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.NodeReplaced()
+  // XXX nsINavHistoryResultViewer.itemReplaced
+  // NHQRN.onVisit()->NHCRN.MergeResults()->NHCRN.ReplaceChildURIAt()->NHRV.ItemReplaced()
 
   // XXX nsINavHistoryResultViewer.invalidateContainer
 
+  // nsINavHistoryResultViewer.invalidateAll
   // nsINavHistoryResultViewer.sortingChanged
-  viewer.invalidatedContainer = null;
   result.sortingMode = options.SORT_BY_TITLE_ASCENDING;
+  do_check_true(viewer.allInvalidated);
   do_check_eq(viewer.sortingMode, options.SORT_BY_TITLE_ASCENDING);
-  do_check_eq(viewer.invalidatedContainer, result.root);
 
   // nsINavHistoryResultViewer.containerClosed
   root.containerOpen = false;
   do_check_eq(viewer.closedContainer, viewer.openedContainer);
   result.viewer = null;
 }