summaryrefslogtreecommitdiffstats
path: root/services/cloudsync/CloudSyncBookmarks.jsm
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /services/cloudsync/CloudSyncBookmarks.jsm
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'services/cloudsync/CloudSyncBookmarks.jsm')
-rw-r--r--services/cloudsync/CloudSyncBookmarks.jsm795
1 files changed, 795 insertions, 0 deletions
diff --git a/services/cloudsync/CloudSyncBookmarks.jsm b/services/cloudsync/CloudSyncBookmarks.jsm
new file mode 100644
index 000000000..bb2e48d59
--- /dev/null
+++ b/services/cloudsync/CloudSyncBookmarks.jsm
@@ -0,0 +1,795 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["Bookmarks"];
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://services-common/utils.js");
+Cu.import("resource://services-crypto/utils.js");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource:///modules/PlacesUIUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+
+Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/CloudSyncPlacesWrapper.jsm");
+Cu.import("resource://gre/modules/CloudSyncEventSource.jsm");
+Cu.import("resource://gre/modules/CloudSyncBookmarksFolderCache.jsm");
+
+const ITEM_TYPES = [
+ "NULL",
+ "BOOKMARK",
+ "FOLDER",
+ "SEPARATOR",
+ "DYNAMIC_CONTAINER", // no longer used by Places, but this ID should not be used for future item types
+];
+
+const CS_UNKNOWN = 0x1;
+const CS_FOLDER = 0x1 << 1;
+const CS_SEPARATOR = 0x1 << 2;
+const CS_QUERY = 0x1 << 3;
+const CS_LIVEMARK = 0x1 << 4;
+const CS_BOOKMARK = 0x1 << 5;
+
+const EXCLUDE_BACKUP_ANNO = "places/excludeFromBackup";
+
+const DATA_VERSION = 1;
+
+function asyncCallback(ctx, func, args) {
+ function invoke() {
+ func.apply(ctx, args);
+ }
+ CommonUtils.nextTick(invoke);
+}
+
+var Record = function (params) {
+ this.id = params.guid;
+ this.parent = params.parent || null;
+ this.index = params.position;
+ this.title = params.title;
+ this.dateAdded = Math.floor(params.dateAdded/1000);
+ this.lastModified = Math.floor(params.lastModified/1000);
+ this.uri = params.url;
+
+ let annos = params.annos || {};
+ Object.defineProperty(this, "annos", {
+ get: function () {
+ return annos;
+ },
+ enumerable: false
+ });
+
+ switch (params.type) {
+ case PlacesUtils.bookmarks.TYPE_FOLDER:
+ if (PlacesUtils.LMANNO_FEEDURI in annos) {
+ this.type = CS_LIVEMARK;
+ this.feed = annos[PlacesUtils.LMANNO_FEEDURI];
+ this.site = annos[PlacesUtils.LMANNO_SITEURI];
+ } else {
+ this.type = CS_FOLDER;
+ }
+ break;
+ case PlacesUtils.bookmarks.TYPE_BOOKMARK:
+ if (this.uri.startsWith("place:")) {
+ this.type = CS_QUERY;
+ } else {
+ this.type = CS_BOOKMARK;
+ }
+ break;
+ case PlacesUtils.bookmarks.TYPE_SEPARATOR:
+ this.type = CS_SEPARATOR;
+ break;
+ default:
+ this.type = CS_UNKNOWN;
+ }
+};
+
+Record.prototype = {
+ version: DATA_VERSION,
+};
+
+var Bookmarks = function () {
+ let createRootFolder = function (name) {
+ let ROOT_FOLDER_ANNO = "cloudsync/rootFolder/" + name;
+ let ROOT_SHORTCUT_ANNO = "cloudsync/rootShortcut/" + name;
+
+ let deferred = Promise.defer();
+ let placesRootId = PlacesUtils.placesRootId;
+ let rootFolderId;
+ let rootShortcutId;
+
+ function createAdapterShortcut(result) {
+ rootFolderId = result;
+ let uri = "place:folder=" + rootFolderId;
+ return PlacesWrapper.insertBookmark(PlacesUIUtils.allBookmarksFolderId, uri,
+ PlacesUtils.bookmarks.DEFAULT_INDEX, name);
+ }
+
+ function setRootFolderCloudSyncAnnotation(result) {
+ rootShortcutId = result;
+ return PlacesWrapper.setItemAnnotation(rootFolderId, ROOT_FOLDER_ANNO,
+ 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+ }
+
+ function setRootShortcutCloudSyncAnnotation() {
+ return PlacesWrapper.setItemAnnotation(rootShortcutId, ROOT_SHORTCUT_ANNO,
+ 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+ }
+
+ function setRootFolderExcludeFromBackupAnnotation() {
+ return PlacesWrapper.setItemAnnotation(rootFolderId, EXCLUDE_BACKUP_ANNO,
+ 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
+ }
+
+ function finish() {
+ deferred.resolve(rootFolderId);
+ }
+
+ Promise.resolve(PlacesUtils.bookmarks.createFolder(placesRootId, name, PlacesUtils.bookmarks.DEFAULT_INDEX))
+ .then(createAdapterShortcut)
+ .then(setRootFolderCloudSyncAnnotation)
+ .then(setRootShortcutCloudSyncAnnotation)
+ .then(setRootFolderExcludeFromBackupAnnotation)
+ .then(finish, deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let getRootFolder = function (name) {
+ let ROOT_FOLDER_ANNO = "cloudsync/rootFolder/" + name;
+ let ROOT_SHORTCUT_ANNO = "cloudsync/rootShortcut/" + name;
+ let deferred = Promise.defer();
+
+ function checkRootFolder(folderIds) {
+ if (!folderIds.length) {
+ return createRootFolder(name);
+ }
+ return Promise.resolve(folderIds[0]);
+ }
+
+ function createFolderObject(folderId) {
+ return new RootFolder(folderId, name);
+ }
+
+ PlacesWrapper.getLocalIdsWithAnnotation(ROOT_FOLDER_ANNO)
+ .then(checkRootFolder, deferred.reject)
+ .then(createFolderObject)
+ .then(deferred.resolve, deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let deleteRootFolder = function (name) {
+ let ROOT_FOLDER_ANNO = "cloudsync/rootFolder/" + name;
+ let ROOT_SHORTCUT_ANNO = "cloudsync/rootShortcut/" + name;
+
+ let deferred = Promise.defer();
+ let placesRootId = PlacesUtils.placesRootId;
+
+ function getRootShortcutId() {
+ return PlacesWrapper.getLocalIdsWithAnnotation(ROOT_SHORTCUT_ANNO);
+ }
+
+ function deleteShortcut(shortcutIds) {
+ if (!shortcutIds.length) {
+ return Promise.resolve();
+ }
+ return PlacesWrapper.removeItem(shortcutIds[0]);
+ }
+
+ function getRootFolderId() {
+ return PlacesWrapper.getLocalIdsWithAnnotation(ROOT_FOLDER_ANNO);
+ }
+
+ function deleteFolder(folderIds) {
+ let deleteFolderDeferred = Promise.defer();
+
+ if (!folderIds.length) {
+ return Promise.resolve();
+ }
+
+ let rootFolderId = folderIds[0];
+ PlacesWrapper.removeFolderChildren(rootFolderId).then(
+ function () {
+ return PlacesWrapper.removeItem(rootFolderId);
+ }
+ ).then(deleteFolderDeferred.resolve, deleteFolderDeferred.reject);
+
+ return deleteFolderDeferred.promise;
+ }
+
+ getRootShortcutId().then(deleteShortcut)
+ .then(getRootFolderId)
+ .then(deleteFolder)
+ .then(deferred.resolve, deferred.reject);
+
+ return deferred.promise;
+ };
+
+ /* PUBLIC API */
+ this.getRootFolder = getRootFolder.bind(this);
+ this.deleteRootFolder = deleteRootFolder.bind(this);
+
+};
+
+this.Bookmarks = Bookmarks;
+
+var RootFolder = function (rootId, rootName) {
+ let suspended = true;
+ let ignoreAll = false;
+
+ let suspend = function () {
+ if (!suspended) {
+ PlacesUtils.bookmarks.removeObserver(observer);
+ suspended = true;
+ }
+ }.bind(this);
+
+ let resume = function () {
+ if (suspended) {
+ PlacesUtils.bookmarks.addObserver(observer, false);
+ suspended = false;
+ }
+ }.bind(this);
+
+ let eventTypes = [
+ "add",
+ "remove",
+ "change",
+ "move",
+ ];
+
+ let eventSource = new EventSource(eventTypes, suspend, resume);
+
+ let folderCache = new FolderCache;
+ folderCache.insert(rootId, null);
+
+ let getCachedFolderIds = function (cache, roots) {
+ let nodes = [...roots];
+ let results = [];
+
+ while (nodes.length) {
+ let node = nodes.shift();
+ results.push(node);
+ let children = cache.getChildren(node);
+ nodes = nodes.concat([...children]);
+ }
+ return results;
+ };
+
+ let getLocalItems = function () {
+ let deferred = Promise.defer();
+
+ let folders = getCachedFolderIds(folderCache, folderCache.getChildren(rootId));
+
+ function getFolders(ids) {
+ let types = [
+ PlacesUtils.bookmarks.TYPE_FOLDER,
+ ];
+ return PlacesWrapper.getItemsById(ids, types);
+ }
+
+ function getContents(parents) {
+ parents.push(rootId);
+ let types = [
+ PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ PlacesUtils.bookmarks.TYPE_SEPARATOR,
+ ];
+ return PlacesWrapper.getItemsByParentId(parents, types)
+ }
+
+ function getParentGuids(results) {
+ results = Array.prototype.concat.apply([], results);
+ let promises = [];
+ results.map(function (result) {
+ let promise = PlacesWrapper.localIdToGuid(result.parent).then(
+ function (guidResult) {
+ result.parent = guidResult;
+ return Promise.resolve(result);
+ },
+ Promise.reject.bind(Promise)
+ );
+ promises.push(promise);
+ });
+ return Promise.all(promises);
+ }
+
+ function getAnnos(results) {
+ results = Array.prototype.concat.apply([], results);
+ let promises = [];
+ results.map(function (result) {
+ let promise = PlacesWrapper.getItemAnnotationsForLocalId(result.id).then(
+ function (annos) {
+ result.annos = annos;
+ return Promise.resolve(result);
+ },
+ Promise.reject.bind(Promise)
+ );
+ promises.push(promise);
+ });
+ return Promise.all(promises);
+ }
+
+ let promises = [
+ getFolders(folders),
+ getContents(folders),
+ ];
+
+ Promise.all(promises)
+ .then(getParentGuids)
+ .then(getAnnos)
+ .then(function (results) {
+ results = results.map((result) => new Record(result));
+ deferred.resolve(results);
+ },
+ deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let getLocalItemsById = function (guids) {
+ let deferred = Promise.defer();
+
+ let types = [
+ PlacesUtils.bookmarks.TYPE_BOOKMARK,
+ PlacesUtils.bookmarks.TYPE_FOLDER,
+ PlacesUtils.bookmarks.TYPE_SEPARATOR,
+ PlacesUtils.bookmarks.TYPE_DYNAMIC_CONTAINER,
+ ];
+
+ function getParentGuids(results) {
+ let promises = [];
+ results.map(function (result) {
+ let promise = PlacesWrapper.localIdToGuid(result.parent).then(
+ function (guidResult) {
+ result.parent = guidResult;
+ return Promise.resolve(result);
+ },
+ Promise.reject.bind(Promise)
+ );
+ promises.push(promise);
+ });
+ return Promise.all(promises);
+ }
+
+ PlacesWrapper.getItemsByGuid(guids, types)
+ .then(getParentGuids)
+ .then(function (results) {
+ results = results.map((result) => new Record(result));
+ deferred.resolve(results);
+ },
+ deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let _createItem = function (item) {
+ let deferred = Promise.defer();
+
+ function getFolderId() {
+ if (item.parent) {
+ return PlacesWrapper.guidToLocalId(item.parent);
+ }
+ return Promise.resolve(rootId);
+ }
+
+ function create(folderId) {
+ let deferred = Promise.defer();
+
+ if (!folderId) {
+ folderId = rootId;
+ }
+ let index = item.hasOwnProperty("index") ? item.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
+
+ function complete(localId) {
+ folderCache.insert(localId, folderId);
+ deferred.resolve(localId);
+ }
+
+ switch (item.type) {
+ case CS_BOOKMARK:
+ case CS_QUERY:
+ PlacesWrapper.insertBookmark(folderId, item.uri, index, item.title, item.id)
+ .then(complete, deferred.reject);
+ break;
+ case CS_FOLDER:
+ PlacesWrapper.createFolder(folderId, item.title, index, item.id)
+ .then(complete, deferred.reject);
+ break;
+ case CS_SEPARATOR:
+ PlacesWrapper.insertSeparator(folderId, index, item.id)
+ .then(complete, deferred.reject);
+ break;
+ case CS_LIVEMARK:
+ let livemark = {
+ title: item.title,
+ parentId: folderId,
+ index: item.index,
+ feedURI: item.feed,
+ siteURI: item.site,
+ guid: item.id,
+ };
+ PlacesUtils.livemarks.addLivemark(livemark)
+ .then(complete, deferred.reject);
+ break;
+ default:
+ deferred.reject("invalid item type: " + item.type);
+ }
+
+ return deferred.promise;
+ }
+
+ getFolderId().then(create)
+ .then(deferred.resolve, deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let _deleteItem = function (item) {
+ let deferred = Promise.defer();
+
+ PlacesWrapper.guidToLocalId(item.id).then(
+ function (localId) {
+ folderCache.remove(localId);
+ return PlacesWrapper.removeItem(localId);
+ }
+ ).then(deferred.resolve, deferred.reject);
+
+ return deferred.promise;
+ };
+
+ let _updateItem = function (item) {
+ let deferred = Promise.defer();
+
+ PlacesWrapper.guidToLocalId(item.id).then(
+ function (localId) {
+ let promises = [];
+
+ if (item.hasOwnProperty("dateAdded")) {
+ promises.push(PlacesWrapper.setItemDateAdded(localId, item.dateAdded));
+ }
+
+ if (item.hasOwnProperty("lastModified")) {
+ promises.push(PlacesWrapper.setItemLastModified(localId, item.lastModified));
+ }
+
+ if ((CS_BOOKMARK | CS_FOLDER) & item.type && item.hasOwnProperty("title")) {
+ promises.push(PlacesWrapper.setItemTitle(localId, item.title));
+ }
+
+ if (CS_BOOKMARK & item.type && item.hasOwnProperty("uri")) {
+ promises.push(PlacesWrapper.changeBookmarkURI(localId, item.uri));
+ }
+
+ if (item.hasOwnProperty("parent")) {
+ let deferred = Promise.defer();
+ PlacesWrapper.guidToLocalId(item.parent)
+ .then(
+ function (parent) {
+ let index = item.hasOwnProperty("index") ? item.index : PlacesUtils.bookmarks.DEFAULT_INDEX;
+ if (CS_FOLDER & item.type) {
+ folderCache.setParent(localId, parent);
+ }
+ return PlacesWrapper.moveItem(localId, parent, index);
+ }
+ )
+ .then(deferred.resolve, deferred.reject);
+ promises.push(deferred.promise);
+ }
+
+ if (item.hasOwnProperty("index") && !item.hasOwnProperty("parent")) {
+ promises.push(Task.spawn(function* () {
+ let localItem = (yield getLocalItemsById([item.id]))[0];
+ let parent = yield PlacesWrapper.guidToLocalId(localItem.parent);
+ let index = item.index;
+ if (CS_FOLDER & item.type) {
+ folderCache.setParent(localId, parent);
+ }
+ yield PlacesWrapper.moveItem(localId, parent, index);
+ }));
+ }
+
+ Promise.all(promises)
+ .then(deferred.resolve, deferred.reject);
+ }
+ );
+
+ return deferred.promise;
+ };
+
+ let mergeRemoteItems = function (items) {
+ ignoreAll = true;
+ let deferred = Promise.defer();
+
+ let newFolders = {};
+ let newItems = [];
+ let updatedItems = [];
+ let deletedItems = [];
+
+ let sortItems = function () {
+ let promises = [];
+
+ let exists = function (item) {
+ let existsDeferred = Promise.defer();
+ if (!item.id) {
+ Object.defineProperty(item, "__exists__", {
+ value: false,
+ enumerable: false
+ });
+ existsDeferred.resolve(item);
+ } else {
+ PlacesWrapper.guidToLocalId(item.id).then(
+ function (localId) {
+ Object.defineProperty(item, "__exists__", {
+ value: localId ? true : false,
+ enumerable: false
+ });
+ existsDeferred.resolve(item);
+ },
+ existsDeferred.reject
+ );
+ }
+ return existsDeferred.promise;
+ }
+
+ let handleSortedItem = function (item) {
+ if (!item.__exists__ && !item.deleted) {
+ if (CS_FOLDER == item.type) {
+ newFolders[item.id] = item;
+ item._children = [];
+ } else {
+ newItems.push(item);
+ }
+ } else if (item.__exists__ && item.deleted) {
+ deletedItems.push(item);
+ } else if (item.__exists__) {
+ updatedItems.push(item);
+ }
+ }
+
+ for (let item of items) {
+ if (!item || 'object' !== typeof(item)) {
+ continue;
+ }
+
+ let promise = exists(item).then(handleSortedItem, Promise.reject.bind(Promise));
+ promises.push(promise);
+ }
+
+ return Promise.all(promises);
+ }
+
+ let processNewFolders = function () {
+ let newFolderGuids = Object.keys(newFolders);
+ let newFolderRoots = [];
+
+ for (let guid of newFolderGuids) {
+ let item = newFolders[guid];
+ if (item.parent && newFolderGuids.indexOf(item.parent) >= 0) {
+ let parent = newFolders[item.parent];
+ parent._children.push(item.id);
+ } else {
+ newFolderRoots.push(guid);
+ }
+ };
+
+ let promises = [];
+ for (let guid of newFolderRoots) {
+ let root = newFolders[guid];
+ let promise = Promise.resolve();
+ promise = promise.then(
+ function () {
+ return _createItem(root);
+ },
+ Promise.reject.bind(Promise)
+ );
+ let items = [].concat(root._children);
+
+ while (items.length) {
+ let item = newFolders[items.shift()];
+ items = items.concat(item._children);
+ promise = promise.then(
+ function () {
+ return _createItem(item);
+ },
+ Promise.reject.bind(Promise)
+ );
+ }
+ promises.push(promise);
+ }
+
+ return Promise.all(promises);
+ }
+
+ let processItems = function () {
+ let promises = [];
+
+ for (let item of newItems) {
+ promises.push(_createItem(item));
+ }
+
+ for (let item of updatedItems) {
+ promises.push(_updateItem(item));
+ }
+
+ for (let item of deletedItems) {
+ _deleteItem(item);
+ }
+
+ return Promise.all(promises);
+ }
+
+ sortItems().then(processNewFolders)
+ .then(processItems)
+ .then(function () {
+ ignoreAll = false;
+ deferred.resolve(items);
+ },
+ function (err) {
+ ignoreAll = false;
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ let ignore = function (id, parent) {
+ if (ignoreAll) {
+ return true;
+ }
+
+ if (rootId == parent || folderCache.has(parent)) {
+ return false;
+ }
+
+ return true;
+ };
+
+ let handleItemAdded = function (id, parent, index, type, uri, title, dateAdded, guid, parentGuid) {
+ let deferred = Promise.defer();
+
+ if (PlacesUtils.bookmarks.TYPE_FOLDER == type) {
+ folderCache.insert(id, parent);
+ }
+
+ eventSource.emit("add", guid);
+ deferred.resolve();
+
+ return deferred.promise;
+ };
+
+ let handleItemRemoved = function (id, parent, index, type, uri, guid, parentGuid) {
+ let deferred = Promise.defer();
+
+ if (PlacesUtils.bookmarks.TYPE_FOLDER == type) {
+ folderCache.remove(id);
+ }
+
+ eventSource.emit("remove", guid);
+ deferred.resolve();
+
+ return deferred.promise;
+ };
+
+ let handleItemChanged = function (id, property, isAnnotation, newValue, lastModified, type, parent, guid, parentGuid) {
+ let deferred = Promise.defer();
+
+ eventSource.emit('change', guid);
+ deferred.resolve();
+
+ return deferred.promise;
+ };
+
+ let handleItemMoved = function (id, oldParent, oldIndex, newParent, newIndex, type, guid, oldParentGuid, newParentGuid) {
+ let deferred = Promise.defer();
+
+ function complete() {
+ eventSource.emit('move', guid);
+ deferred.resolve();
+ }
+
+ if (PlacesUtils.bookmarks.TYPE_FOLDER != type) {
+ complete();
+ return deferred.promise;
+ }
+
+ if (folderCache.has(oldParent) && folderCache.has(newParent)) {
+ // Folder move inside cloudSync root, so just update parents/children.
+ folderCache.setParent(id, newParent);
+ complete();
+ } else if (!folderCache.has(oldParent)) {
+ // Folder moved in from ouside cloudSync root.
+ PlacesWrapper.updateCachedFolderIds(folderCache, newParent)
+ .then(complete, complete);
+ } else if (!folderCache.has(newParent)) {
+ // Folder moved out from inside cloudSync root.
+ PlacesWrapper.updateCachedFolderIds(folderCache, oldParent)
+ .then(complete, complete);
+ }
+
+ return deferred.promise;
+ };
+
+ let observer = {
+ onBeginBatchUpdate: function () {
+ },
+
+ onEndBatchUpdate: function () {
+ },
+
+ onItemAdded: function (id, parent, index, type, uri, title, dateAdded, guid, parentGuid) {
+ if (ignore(id, parent)) {
+ return;
+ }
+
+ asyncCallback(this, handleItemAdded, Array.prototype.slice.call(arguments));
+ },
+
+ onItemRemoved: function (id, parent, index, type, uri, guid, parentGuid) {
+ if (ignore(id, parent)) {
+ return;
+ }
+
+ asyncCallback(this, handleItemRemoved, Array.prototype.slice.call(arguments));
+ },
+
+ onItemChanged: function (id, property, isAnnotation, newValue, lastModified, type, parent, guid, parentGuid) {
+ if (ignore(id, parent)) {
+ return;
+ }
+
+ asyncCallback(this, handleItemChanged, Array.prototype.slice.call(arguments));
+ },
+
+ onItemMoved: function (id, oldParent, oldIndex, newParent, newIndex, type, guid, oldParentGuid, newParentGuid) {
+ if (ignore(id, oldParent) && ignore(id, newParent)) {
+ return;
+ }
+
+ asyncCallback(this, handleItemMoved, Array.prototype.slice.call(arguments));
+ }
+ };
+
+ /* PUBLIC API */
+ this.addEventListener = eventSource.addEventListener;
+ this.removeEventListener = eventSource.removeEventListener;
+ this.getLocalItems = getLocalItems.bind(this);
+ this.getLocalItemsById = getLocalItemsById.bind(this);
+ this.mergeRemoteItems = mergeRemoteItems.bind(this);
+
+ let rootGuid = null; // resolved before becoming ready (below)
+ this.__defineGetter__("id", function () {
+ return rootGuid;
+ });
+ this.__defineGetter__("name", function () {
+ return rootName;
+ });
+
+ let deferred = Promise.defer();
+ let getGuidForRootFolder = function () {
+ return PlacesWrapper.localIdToGuid(rootId);
+ }
+ PlacesWrapper.updateCachedFolderIds(folderCache, rootId)
+ .then(getGuidForRootFolder, getGuidForRootFolder)
+ .then(function (guid) {
+ rootGuid = guid;
+ deferred.resolve(this);
+ }.bind(this),
+ deferred.reject);
+ return deferred.promise;
+};
+
+RootFolder.prototype = {
+ BOOKMARK: CS_BOOKMARK,
+ FOLDER: CS_FOLDER,
+ SEPARATOR: CS_SEPARATOR,
+ QUERY: CS_QUERY,
+ LIVEMARK: CS_LIVEMARK,
+};