summaryrefslogtreecommitdiffstats
path: root/services/cloudsync/CloudSyncBookmarks.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'services/cloudsync/CloudSyncBookmarks.jsm')
-rw-r--r--services/cloudsync/CloudSyncBookmarks.jsm795
1 files changed, 0 insertions, 795 deletions
diff --git a/services/cloudsync/CloudSyncBookmarks.jsm b/services/cloudsync/CloudSyncBookmarks.jsm
deleted file mode 100644
index bb2e48d59..000000000
--- a/services/cloudsync/CloudSyncBookmarks.jsm
+++ /dev/null
@@ -1,795 +0,0 @@
-/* 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,
-};