Various engine/bookmark changes:
authorDan Mills <thunder@mozilla.com>
Tue, 23 Dec 2008 11:30:31 -0800
changeset 45134 88e7710800ff3797663840220417c821be365159
parent 45133 494cfdfda080e057d37d14097e7301e865b01ca0
child 45135 4361218bc588c434f96b61f9f1cf042f4a71d3c7
push idunknown
push userunknown
push dateunknown
Various engine/bookmark changes: * Rely on the server to sort incoming records, remove all sorting code client-side. * Streamline sync to be able to process incoming records one at a time, as soon as they are downloaded. This changes reconciliation to be able to process a single incoming record. * Engine base class will automatically convert parentid, depth, and sortindex into toplevel WBO objects. This is good for now but kinda broken, engine subclasses should really be generating WBO records themselves. * Since index is now a toplevel WBO property, there is no need for the bookmarks sync code to subclass recordLike. * Refactor bookmarks store to be a little cleaner and work directly with records instead of "commands".
services/sync/modules/engines.js
services/sync/modules/engines/bookmarks.js
--- a/services/sync/modules/engines.js
+++ b/services/sync/modules/engines.js
@@ -292,64 +292,59 @@ SyncEngine.prototype = {
   _createRecord: function SyncEngine__createRecord(id, encrypt) {
     let self = yield;
 
     let record = new CryptoWrapper();
     record.uri = this.engineURL + id;
     record.encryption = this.cryptoMetaURL;
     record.cleartext = this._store.wrapItem(id);
 
-    if (record.cleartext && record.cleartext.parentid)
+    // XXX subclasses might not expect us to delete the payload fields
+    if (record.cleartext) {
+      if (record.cleartext.parentid) {
         record.parentid = record.cleartext.parentid;
+        delete record.cleartext.parentid;
+      }
+      if (typeof(record.cleartext.depth) != "undefined") {
+        record.depth = record.cleartext.depth;
+        delete record.cleartext.depth;
+      }
+      if (typeof(record.cleartext.sortindex) != "undefined") {
+        record.sortindex = record.cleartext.sortindex;
+        delete record.cleartext.sortindex;
+      }
+    }
 
     if (encrypt || encrypt == undefined)
       yield record.encrypt(self.cb, ID.get('WeaveCryptoID').password);
 
     self.done(record);
   },
 
   // Check if a record is "like" another one, even though the IDs are different,
   // in that case, we'll change the ID of the local item to match
   // Probably needs to be overridden in a subclass, to change which criteria
   // make two records "the same one"
   _recordLike: function SyncEngine__recordLike(a, b) {
     if (a.parentid != b.parentid)
       return false;
+    if (a.depth != b.depth)
+      return false;
+    // note: sortindex ignored
     return Utils.deepEquals(a.cleartext, b.cleartext);
   },
 
   _changeRecordRefs: function SyncEngine__changeRecordRefs(oldID, newID) {
     let self = yield;
     for each (let rec in this.outgoing) {
       if (rec.parentid == oldID)
         rec.parentid = newID;
     }
   },
 
-  _recDepth: function SyncEngine__recDepth(rec) {
-    // we've calculated depth for this record already
-    if (rec.depth)
-      return rec.depth;
-
-    // record has no parent
-    if (!rec.parentid)
-      return 0;
-
-    // search for the record's parent and calculate its depth, then add one
-    for each (let inc in this.incoming) {
-      if (inc.id == rec.parentid) {
-        rec.depth = this._recDepth(inc) + 1;
-        return rec.depth;
-      }
-    }
-
-    // we couldn't find the record's parent, so it's an orphan
-    return 0;
-  },
-
   // Any setup that needs to happen at the beginning of each sync.
   // Makes sure crypto records and keys are all set-up
   _syncStartup: function SyncEngine__syncStartup() {
     let self = yield;
 
     this._log.debug("Ensuring server crypto records are there");
 
     let meta = yield CryptoMetas.get(self.cb, this.cryptoMetaURL);
@@ -421,16 +416,18 @@ SyncEngine.prototype = {
     let item;
     while ((item = yield newitems.iter.next(self.cb))) {
       yield item.decrypt(self.cb, ID.get('WeaveCryptoID').password);
       if (yield this._reconcile.async(this, self.cb, item))
         yield this._applyIncoming.async(this, self.cb, item);
       else
         this._log.debug("Skipping reconciled incoming item");
     }
+    if (typeof(this._lastSyncTmp) == "string")
+      this._lastSyncTmp = parseInt(this._lastSyncTmp);
     if (this.lastSync < this._lastSyncTmp)
         this.lastSync = this._lastSyncTmp;
 
     // removes any holes caused by reconciliation above:
     this._outgoing = this.outgoing.filter(function(n) n);
   },
 
   // Reconciliation has two steps:
@@ -491,42 +488,65 @@ SyncEngine.prototype = {
     }
     self.done(true);
   },
 
   // Apply incoming records
   _applyIncoming: function SyncEngine__applyIncoming(item) {
     let self = yield;
     this._log.debug("Applying incoming record");
-    this._log.trace("Incoming record: " + this._json.encode(item.cleartext));
+    this._log.trace("Incoming:\n" + item);
     try {
       yield this._store.applyIncoming(self.cb, item);
-      if (item.modified > this._lastSyncTmp)
+      if (this._lastSyncTmp < item.modified)
         this._lastSyncTmp = item.modified;
     } catch (e) {
       this._log.warn("Error while applying incoming record: " +
                      (e.message? e.message : e));
     }
   },
 
   // Upload outgoing records
   _uploadOutgoing: function SyncEngine__uploadOutgoing() {
     let self = yield;
+
     if (this.outgoing.length) {
-      this._log.debug("Uploading client changes");
+      this._log.debug("Uploading client changes (" + this.outgoing.length + ")");
+
+      // collection we'll upload
       let up = new Collection(this.engineURL);
+
+      // regen the store cache so we can get item depths
+      this._store.cacheItemsHint();
+      let depth = {};
+
       let out;
       while ((out = this.outgoing.pop())) {
-        this._log.trace("Outgoing record: " + this._json.encode(out.cleartext));
+        this._log.trace("Outgoing:\n" + out);
         yield out.encrypt(self.cb, ID.get('WeaveCryptoID').password);
         yield up.pushRecord(self.cb, out);
+        this._store.wrapDepth(out.id, depth);
       }
+
+      // now add short depth-only records
+      this._log.trace(depth.length + "outgoing depth records");
+      for (let id in depth) {
+        up.pushDepthRecord({id: id, depth: depth[id]});
+      }
+      this._store.clearItemCacheHint();
+
+      // do the upload
       yield up.post(self.cb);
-      if (up.data.modified > this.lastSync)
-        this.lastSync = up.data.modified;
+
+      // save last modified date
+      let mod = up.data.modified;
+      if (typeof(mod) == "string")
+        mod = parseInt(mod);
+      if (mod > this.lastSync)
+        this.lastSync = mod;
     }
     this._tracker.clearChangedIDs();
   },
 
   // Any cleanup necessary.
   // Save the current snapshot so as to calculate changes at next sync
   _syncFinish: function SyncEngine__syncFinish(error) {
     let self = yield;
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -86,47 +86,17 @@ BookmarksEngine.prototype = {
     this.__defineGetter__("_store", function() store);
     return store;
   },
 
   get _tracker() {
     let tracker = new BookmarksTracker();
     this.__defineGetter__("_tracker", function() tracker);
     return tracker;
-  },
-
-  _recordLike: function SyncEngine__recordLike(a, b) {
-    if (a.parentid != b.parentid)
-      return false;
-    for (let key in a.cleartext) {
-      if (key == "index")
-        continue;
-      if (!Utils.deepEquals(a.cleartext[key], b.cleartext[key]))
-        return false;
-    }
-    for (key in b.cleartext) {
-      if (key == "index")
-        continue;
-      if (!Utils.deepEquals(a.cleartext[key], b.cleartext[key]))
-        return false;
-    }
-    return true;
-  },
-
-  _changeRecordRefs: function BmkEngine__changeRecordRefs(oldID, newID) {
-    let self = yield;
-    for each (let rec in this.outgoing) {
-      if (rec.parentid == oldID) {
-        rec.parentid = newID;
-        rec.cleartext.parentid = newID;
-        yield rec.encrypt(self.cb, ID.get('WeaveCryptoID').password);
-      }
-    }
   }
-
   // XXX for sharing, will need to re-add code to get new shares before syncing,
   //     and updating incoming/outgoing shared folders after syncing
 };
 
 function BookmarksStore() {
   this._init();
 }
 BookmarksStore.prototype = {
@@ -202,122 +172,119 @@ BookmarksStore.prototype = {
   },
 
   _itemExists: function BStore__itemExists(id) {
     return this._getItemIdForGUID(id) >= 0;
   },
 
   create: function BStore_create(record) {
     let newId;
-    let command = {GUID: record.id, data: record.cleartext};
-    let parentId = this._getItemIdForGUID(command.data.parentid);
+    let parentId = this._getItemIdForGUID(record.parentid);
 
     if (parentId < 0) {
       this._log.warn("Creating node with unknown parent -> reparenting to root");
       parentId = this._bms.bookmarksMenuFolder;
     }
 
-    switch (command.data.type) {
+    switch (record.cleartext.type) {
     case "query":
     case "bookmark":
     case "microsummary": {
-      this._log.debug(" -> creating bookmark \"" + command.data.title + "\"");
-      let URI = Utils.makeURI(command.data.URI);
-      newId = this._bms.insertBookmark(parentId,
-                                       URI,
-                                       command.data.index,
-                                       command.data.title);
+      this._log.debug(" -> creating bookmark \"" + record.cleartext.title + "\"");
+      let URI = Utils.makeURI(record.cleartext.URI);
+      newId = this._bms.insertBookmark(parentId, URI, record.sortindex,
+                                       record.cleartext.title);
       this._ts.untagURI(URI, null);
-      this._ts.tagURI(URI, command.data.tags);
-      this._bms.setKeywordForBookmark(newId, command.data.keyword);
-      if (command.data.description) {
+      this._ts.tagURI(URI, record.cleartext.tags);
+      this._bms.setKeywordForBookmark(newId, record.cleartext.keyword);
+      if (record.cleartext.description) {
         this._ans.setItemAnnotation(newId, "bookmarkProperties/description",
-                                    command.data.description, 0,
+                                    record.cleartext.description, 0,
                                    this._ans.EXPIRE_NEVER);
       }
 
-      if (command.data.type == "microsummary") {
+      if (record.cleartext.type == "microsummary") {
         this._log.debug("   \-> is a microsummary");
         this._ans.setItemAnnotation(newId, "bookmarks/staticTitle",
-                                    command.data.staticTitle || "", 0, this._ans.EXPIRE_NEVER);
-        let genURI = Utils.makeURI(command.data.generatorURI);
+                                    record.cleartext.staticTitle || "", 0, this._ans.EXPIRE_NEVER);
+        let genURI = Utils.makeURI(record.cleartext.generatorURI);
 	if (this._ms == SERVICE_NOT_SUPPORTED) {
 	  this._log.warn("Can't create microsummary -- not supported.");
 	} else {
           try {
             let micsum = this._ms.createMicrosummary(URI, genURI);
             this._ms.setMicrosummary(newId, micsum);
           }
           catch(ex) { /* ignore "missing local generator" exceptions */ }
 	}
       }
     } break;
     case "folder":
-      this._log.debug(" -> creating folder \"" + command.data.title + "\"");
+      this._log.debug(" -> creating folder \"" + record.cleartext.title + "\"");
       newId = this._bms.createFolder(parentId,
-                                     command.data.title,
-                                     command.data.index);
+                                     record.cleartext.title,
+                                     record.sortindex);
       // If folder is an outgoing share, put the annotations on it:
-      if ( command.data.outgoingSharedAnno != undefined ) {
+      if ( record.cleartext.outgoingSharedAnno != undefined ) {
 	this._ans.setItemAnnotation(newId,
 				    OUTGOING_SHARED_ANNO,
-                                    command.data.outgoingSharedAnno,
+                                    record.cleartext.outgoingSharedAnno,
 				    0,
 				    this._ans.EXPIRE_NEVER);
 	this._ans.setItemAnnotation(newId,
 				    SERVER_PATH_ANNO,
-                                    command.data.serverPathAnno,
+                                    record.cleartext.serverPathAnno,
 				    0,
 				    this._ans.EXPIRE_NEVER);
 
       }
       break;
     case "livemark":
-      this._log.debug(" -> creating livemark \"" + command.data.title + "\"");
+      this._log.debug(" -> creating livemark \"" + record.cleartext.title + "\"");
       newId = this._ls.createLivemark(parentId,
-                                      command.data.title,
-                                      Utils.makeURI(command.data.siteURI),
-                                      Utils.makeURI(command.data.feedURI),
-                                      command.data.index);
+                                      record.cleartext.title,
+                                      Utils.makeURI(record.cleartext.siteURI),
+                                      Utils.makeURI(record.cleartext.feedURI),
+                                      record.sortindex);
       break;
     case "incoming-share":
       /* even though incoming shares are folders according to the
        * bookmarkService, _wrap() wraps them as type=incoming-share, so we
        * handle them separately, like so: */
-      this._log.debug(" -> creating incoming-share \"" + command.data.title + "\"");
+      this._log.debug(" -> creating incoming-share \"" + record.cleartext.title + "\"");
       newId = this._bms.createFolder(parentId,
-                                     command.data.title,
-                                     command.data.index);
+                                     record.cleartext.title,
+                                     record.sortindex);
       this._ans.setItemAnnotation(newId,
 				  INCOMING_SHARED_ANNO,
-                                  command.data.incomingSharedAnno,
+                                  record.cleartext.incomingSharedAnno,
 				  0,
 				  this._ans.EXPIRE_NEVER);
       this._ans.setItemAnnotation(newId,
 				  SERVER_PATH_ANNO,
-                                  command.data.serverPathAnno,
+                                  record.cleartext.serverPathAnno,
 				  0,
 				  this._ans.EXPIRE_NEVER);
       break;
     case "separator":
       this._log.debug(" -> creating separator");
-      newId = this._bms.insertSeparator(parentId, command.data.index);
+      newId = this._bms.insertSeparator(parentId, record.sortindex);
       break;
     default:
-      this._log.error("_createCommand: Unknown item type: " + command.data.type);
+      this._log.error("_create: Unknown item type: " + record.cleartext.type);
       break;
     }
     if (newId) {
-      this._log.trace("Setting GUID of new item " + newId + " to " + command.GUID);
+      this._log.trace("Setting GUID of new item " + newId + " to " + record.id);
       let cur = this._bms.getItemGUID(newId);
-      if (cur == command.GUID)
-        this._log.warn("Item " + newId + " already has GUID " + command.GUID);
+      if (cur == record.id)
+        this._log.warn("Item " + newId + " already has GUID " + record.id);
       else {
-        this._bms.setItemGUID(newId, command.GUID);
-        Engines.get("bookmarks")._tracker._all[newId] = command.GUID; // HACK - see tracker
+        this._bms.setItemGUID(newId, record.id);
+        Engines.get("bookmarks")._tracker._all[newId] = record.id; // HACK - see tracker
       }
     }
   },
 
   remove: function BStore_remove(record) {
     if (record.id == "menu" ||
         record.id == "toolbar" ||
         record.id == "unfiled") {
@@ -348,121 +315,93 @@ BookmarksStore.prototype = {
       break;
     default:
       this._log.error("remove: Unknown item type: " + type);
       break;
     }
   },
 
   update: function BStore_update(record) {
-    let command = {GUID: record.id, data: record.cleartext};
+    let itemId = this._getItemIdForGUID(record.id);
 
-    if (command.GUID == "menu" ||
-        command.GUID == "toolbar" ||
-        command.GUID == "unfiled") {
-      this._log.debug("Attempted to edit root node (" + command.GUID +
-                      ").  Skipping command.");
+    if (record.id == "menu" ||
+        record.id == "toolbar" ||
+        record.id == "unfiled") {
+      this._log.debug("Skipping update for root node.");
+      return;
+    }
+    if (itemId < 0) {
+      this._log.debug("Skipping update for unknown item: " + record.id);
       return;
     }
 
-    var itemId = this._getItemIdForGUID(command.GUID);
-    if (itemId < 0) {
-      this._log.debug("Item for GUID " + command.GUID + " not found.  Skipping.");
-      return;
-    }
-    this._log.trace("Editing item " + itemId);
+    this._log.trace("Updating " + record.id + " (" + itemId + ")");
 
-    for (let key in command.data) {
+    if ((this._bms.getItemIndex(itemId) != record.sortindex) ||
+        (this._bms.getFolderIdForItem(itemId) !=
+         this._getItemIdForGUID(record.parentid))) {
+      this._log.trace("Moving item (changing folder/index)");
+      let parentid = this._getItemIdForGUID(record.parentid);
+      this._bms.moveItem(itemId, parentid, record.sortindex);
+    }
+
+    for (let key in record.cleartext) {
       switch (key) {
-      case "type":
-        // all commands have this to help in reconciliation, but it makes
-        // no sense to edit it
-        break;
       case "title":
-        this._bms.setItemTitle(itemId, command.data.title);
+        this._bms.setItemTitle(itemId, record.cleartext.title);
         break;
       case "URI":
-        this._bms.changeBookmarkURI(itemId, Utils.makeURI(command.data.URI));
+        this._bms.changeBookmarkURI(itemId, Utils.makeURI(record.cleartext.URI));
         break;
-      case "index":
-        let curIdx = this._bms.getItemIndex(itemId);
-        if (curIdx != command.data.index) {
-          // ignore index if we're going to move the item to another folder altogether
-          if (command.data.parentid &&
-              (this._bms.getFolderIdForItem(itemId) !=
-               this._getItemIdForGUID(command.data.parentid)))
-            break;
-          this._log.trace("Moving item (changing index)");
-          this._bms.moveItem(itemId, this._bms.getFolderIdForItem(itemId),
-                             command.data.index);
-        }
-        break;
-      case "parentid": {
-        if (command.data.parentid &&
-            (this._bms.getFolderIdForItem(itemId) !=
-             this._getItemIdForGUID(command.data.parentid))) {
-          this._log.trace("Moving item (changing folder)");
-          let index = -1;
-          if (command.data.index && command.data.index >= 0)
-            index = command.data.index;
-          this._bms.moveItem(itemId,
-                             this._getItemIdForGUID(command.data.parentid), index);
-        }
-      } break;
       case "tags": {
         // filter out null/undefined/empty tags
-        let tags = command.data.tags.filter(function(t) t);
+        let tags = record.cleartext.tags.filter(function(t) t);
         let tagsURI = this._bms.getBookmarkURI(itemId);
         this._ts.untagURI(tagsURI, null);
         this._ts.tagURI(tagsURI, tags);
       } break;
       case "keyword":
-        this._bms.setKeywordForBookmark(itemId, command.data.keyword);
+        this._bms.setKeywordForBookmark(itemId, record.cleartext.keyword);
         break;
       case "description":
-        if (command.data.description) {
-          this._ans.setItemAnnotation(itemId, "bookmarkProperties/description",
-                                      command.data.description, 0,
-                                      this._ans.EXPIRE_NEVER);
-        }
+        this._ans.setItemAnnotation(itemId, "bookmarkProperties/description",
+                                    record.cleartext.description, 0,
+                                    this._ans.EXPIRE_NEVER);
         break;
       case "generatorURI": {
         let micsumURI = Utils.makeURI(this._bms.getBookmarkURI(itemId));
-        let genURI = Utils.makeURI(command.data.generatorURI);
+        let genURI = Utils.makeURI(record.cleartext.generatorURI);
 	if (this._ms == SERVICE_NOT_SUPPORTED) {
 	  this._log.warn("Can't create microsummary -- not supported.");
 	} else {
           let micsum = this._ms.createMicrosummary(micsumURI, genURI);
           this._ms.setMicrosummary(itemId, micsum);
 	}
       } break;
       case "siteURI":
-        this._ls.setSiteURI(itemId, Utils.makeURI(command.data.siteURI));
+        this._ls.setSiteURI(itemId, Utils.makeURI(record.cleartext.siteURI));
         break;
       case "feedURI":
-        this._ls.setFeedURI(itemId, Utils.makeURI(command.data.feedURI));
+        this._ls.setFeedURI(itemId, Utils.makeURI(record.cleartext.feedURI));
         break;
       case "outgoingSharedAnno":
 	this._ans.setItemAnnotation(itemId, OUTGOING_SHARED_ANNO,
-				    command.data.outgoingSharedAnno, 0,
+				    record.cleartext.outgoingSharedAnno, 0,
 				    this._ans.EXPIRE_NEVER);
 	break;
       case "incomingSharedAnno":
 	this._ans.setItemAnnotation(itemId, INCOMING_SHARED_ANNO,
-				    command.data.incomingSharedAnno, 0,
+				    record.cleartext.incomingSharedAnno, 0,
 				    this._ans.EXPIRE_NEVER);
 	break;
       case "serverPathAnno":
 	this._ans.setItemAnnotation(itemId, SERVER_PATH_ANNO,
-				    command.data.serverPathAnno, 0,
+				    record.cleartext.serverPathAnno, 0,
 				    this._ans.EXPIRE_NEVER);
 	break;
-      default:
-        this._log.warn("Can't change item property: " + key);
-        break;
       }
     }
   },
 
   changeItemID: function BStore_changeItemID(oldID, newID) {
     var itemId = this._getItemIdForGUID(oldID);
     if (itemId == null) // toplevel folder
       return;
@@ -485,26 +424,26 @@ BookmarksStore.prototype = {
   },
 
   _getNode: function BSS__getNode(folder) {
     let query = this._hsvc.getNewQuery();
     query.setFolders([folder], 1);
     return this._hsvc.executeQuery(query, this._hsvc.getNewQueryOptions()).root;
   },
 
-  __wrap: function BSS___wrap(node, items, parentid, index, guidOverride) {
+  __wrap: function BSS___wrap(node, items, parentid, index, depth, guidOverride) {
     let GUID, item;
 
     // we override the guid for the root items, "menu", "toolbar", etc.
     if (guidOverride) {
       GUID = guidOverride;
-      item = {};
+      item = {sortindex: index, depth: depth};
     } else {
       GUID = this._bms.getItemGUID(node.itemId);
-      item = {parentid: parentid, index: index};
+      item = {parentid: parentid, sortindex: index, depth: depth};
     }
 
     if (node.type == node.RESULT_TYPE_FOLDER) {
       if (this._ls.isLivemark(node.itemId)) {
         item.type = "livemark";
         let siteURI = this._ls.getSiteURI(node.itemId);
         let feedURI = this._ls.getFeedURI(node.itemId);
         item.siteURI = siteURI? siteURI.spec : "";
@@ -529,17 +468,17 @@ BookmarksStore.prototype = {
                                                       OUTGOING_SHARED_ANNO);
 	}
 	if (this._ans.itemHasAnnotation(node.itemId, SERVER_PATH_ANNO)) {
 	  item.serverPathAnno = this._ans.getItemAnnotation(node.itemId,
 							    SERVER_PATH_ANNO);
 	}
 
         for (var i = 0; i < node.childCount; i++) {
-          this.__wrap(node.getChild(i), items, GUID, i);
+          this.__wrap(node.getChild(i), items, GUID, i, depth + 1);
         }
       }
       if (!guidOverride)
         item.title = node.title; // no titles for root nodes
 
     } else if (node.type == node.RESULT_TYPE_URI ||
                node.type == node.RESULT_TYPE_QUERY) {
       if (this._ms != SERVICE_NOT_SUPPORTED &&
@@ -596,57 +535,17 @@ BookmarksStore.prototype = {
       return;
     }
 
     items[GUID] = item;
   },
 
   // helper
   _wrap: function BStore__wrap(node, items, rootName) {
-    return this.__wrap(node, items, null, null, rootName);
-  },
-
-  _wrapMountOutgoing: function BStore__wrapById( itemId ) {
-    let node = this._getNode(itemId);
-    if (node.type != node.RESULT_TYPE_FOLDER)
-      throw "Trying to wrap a non-folder mounted share";
-
-    let GUID = this._bms.getItemGUID(itemId);
-    let snapshot = {};
-    node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
-    node.containerOpen = true;
-    for (var i = 0; i < node.childCount; i++) {
-      this.__wrap(node.getChild(i), snapshot, GUID, i);
-    }
-
-    // remove any share mountpoints
-    for (let guid in snapshot) {
-      // TODO decide what to do with this...
-      if (snapshot[guid].type == "incoming-share")
-        delete snapshot[guid];
-    }
-    return snapshot;
-  },
-
-  findIncomingShares: function BStore_findIncomingShares() {
-    /* Returns list of mount data structures, each of which
-       represents one incoming shared-bookmark folder. */
-    let ret = [];
-    let a = this._ans.getItemsWithAnnotation(INCOMING_SHARED_ANNO, {});
-    for (let i = 0; i < a.length; i++) {
-      /* The value of the incoming-shared annotation is the id of the
-       person who has shared it with us.  Get that value: */
-      let userId = this._ans.getItemAnnotation(a[i], INCOMING_SHARED_ANNO);
-      let node = this._getNode(a[i]);
-      let GUID = this._bms.getItemGUID(a[i]);
-      let path = this._ans.getItemAnnotation(a[i], SERVER_PATH_ANNO);
-      let dat = {rootGUID: GUID, userid: userId, serverPath: path, node: node};
-      ret.push(dat);
-    }
-    return ret;
+    return this.__wrap(node, items, null, 0, 0, rootName);
   },
 
   wrap: function BStore_wrap() {
     var items = {};
     this._wrap(this._getNode(this._bms.bookmarksMenuFolder), items, "menu");
     this._wrap(this._getNode(this._bms.toolbarFolder), items, "toolbar");
     this._wrap(this._getNode(this._bms.unfiledBookmarksFolder), items, "unfiled");
     this._lookup = items;