Remove CloudSync

Tag #812
pull/7/head
wolfbeast 3 years ago committed by Roy Tam
parent 21193dac4c
commit 3685261ed4
  1. 11
      application/basilisk/base/content/browser-syncui.js
  2. 14
      application/basilisk/base/content/sync/aboutSyncTabs.js
  3. 4
      application/palemoon/configure.in
  4. 7
      old-configure.in
  5. 89
      services/cloudsync/CloudSync.jsm
  6. 88
      services/cloudsync/CloudSyncAdapters.jsm
  7. 795
      services/cloudsync/CloudSyncBookmarks.jsm
  8. 105
      services/cloudsync/CloudSyncBookmarksFolderCache.jsm
  9. 65
      services/cloudsync/CloudSyncEventSource.jsm
  10. 87
      services/cloudsync/CloudSyncLocal.jsm
  11. 375
      services/cloudsync/CloudSyncPlacesWrapper.jsm
  12. 318
      services/cloudsync/CloudSyncTabs.jsm
  13. 234
      services/cloudsync/docs/api.md
  14. 54
      services/cloudsync/docs/architecture.rst
  15. 77
      services/cloudsync/docs/dataformat.rst
  16. 132
      services/cloudsync/docs/example.rst
  17. 19
      services/cloudsync/docs/index.rst
  18. 21
      services/cloudsync/moz.build
  19. 5
      services/cloudsync/tests/mochitest/browser.ini
  20. 79
      services/cloudsync/tests/mochitest/browser_tabEvents.js
  21. 7
      services/cloudsync/tests/mochitest/other_window.html
  22. 10
      services/cloudsync/tests/xpcshell/head.js
  23. 73
      services/cloudsync/tests/xpcshell/test_bookmarks.js
  24. 18
      services/cloudsync/tests/xpcshell/test_lazyload.js
  25. 19
      services/cloudsync/tests/xpcshell/test_module.js
  26. 29
      services/cloudsync/tests/xpcshell/test_tabs.js
  27. 10
      services/cloudsync/tests/xpcshell/xpcshell.ini
  28. 3
      services/moz.build
  29. 7
      toolkit/modules/AppConstants.jsm
  30. 3
      toolkit/modules/moz.build

@ -4,11 +4,6 @@
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
#ifdef MOZ_SERVICES_CLOUDSYNC
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
"resource://gre/modules/CloudSync.jsm");
#endif
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
@ -171,13 +166,7 @@ var gSyncUI = {
document.getElementById("sync-setup-state").hidden = true;
document.getElementById("sync-syncnow-state").hidden = true;
#ifdef MOZ_SERVICES_CLOUDSYNC
if (CloudSync && CloudSync.ready && CloudSync().adapters.count) {
document.getElementById("sync-syncnow-state").hidden = false;
} else if (loginFailed) {
#else
if (loginFailed) {
#endif
// unhiding this element makes the menubar show the login failure state.
document.getElementById("sync-reauth-state").hidden = false;
} else if (needsSetup) {

@ -14,11 +14,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
#ifdef MOZ_SERVICES_CLOUDSYNC
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
"resource://gre/modules/CloudSync.jsm");
#endif
var RemoteTabViewer = {
_tabsList: null,
@ -183,16 +178,7 @@ var RemoteTabViewer = {
}
}
#ifdef MOZ_SERVICES_CLOUDSYNC
if (CloudSync && CloudSync.ready && CloudSync().tabsReady && CloudSync().tabs.hasRemoteTabs()) {
this._generateCloudSyncTabList()
.then(complete, complete);
} else {
complete();
}
#else
complete();
#endif
},
_clearTabList: function () {

@ -36,7 +36,3 @@ MOZ_ARG_DISABLE_BOOL(sync,
MOZ_SERVICES_SYNC=,
MOZ_SERVICES_SYNC=1)
if test -z "$MOZ_SERVICES_SYNC"; then
MOZ_SERVICES_CLOUDSYNC=
fi

@ -2255,7 +2255,6 @@ MOZ_DEVTOOLS=
MOZ_PLACES=1
MOZ_SERVICES_HEALTHREPORT=1
MOZ_SERVICES_SYNC=1
MOZ_SERVICES_CLOUDSYNC=1
MOZ_USERINFO=1
case "$target_os" in
@ -5093,12 +5092,6 @@ if test -n "$MOZ_SERVICES_SYNC"; then
AC_DEFINE(MOZ_SERVICES_SYNC)
fi
dnl Build Services/CloudSync if required
AC_SUBST(MOZ_SERVICES_CLOUDSYNC)
if test -n "$MOZ_SERVICES_CLOUDSYNC"; then
AC_DEFINE(MOZ_SERVICES_CLOUDSYNC)
fi
dnl ========================================================
if test "$MOZ_DEBUG"; then

@ -1,89 +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 = ["CloudSync"];
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Adapters",
"resource://gre/modules/CloudSyncAdapters.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Local",
"resource://gre/modules/CloudSyncLocal.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Bookmarks",
"resource://gre/modules/CloudSyncBookmarks.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Tabs",
"resource://gre/modules/CloudSyncTabs.jsm");
var API_VERSION = 1;
var _CloudSync = function () {
};
_CloudSync.prototype = {
_adapters: null,
get adapters () {
if (!this._adapters) {
this._adapters = new Adapters();
}
return this._adapters;
},
_bookmarks: null,
get bookmarks () {
if (!this._bookmarks) {
this._bookmarks = new Bookmarks();
}
return this._bookmarks;
},
_local: null,
get local () {
if (!this._local) {
this._local = new Local();
}
return this._local;
},
_tabs: null,
get tabs () {
if (!this._tabs) {
this._tabs = new Tabs();
}
return this._tabs;
},
get tabsReady () {
return this._tabs ? true: false;
},
get version () {
return API_VERSION;
},
};
this.CloudSync = function CloudSync () {
return _cloudSyncInternal.instance;
};
Object.defineProperty(CloudSync, "ready", {
get: function () {
return _cloudSyncInternal.ready;
}
});
var _cloudSyncInternal = {
instance: null,
ready: false,
};
XPCOMUtils.defineLazyGetter(_cloudSyncInternal, "instance", function () {
_cloudSyncInternal.ready = true;
return new _CloudSync();
}.bind(this));

@ -1,88 +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 = ["Adapters"];
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/CloudSyncEventSource.jsm");
this.Adapters = function () {
let eventTypes = [
"sync",
];
let suspended = true;
let suspend = function () {
if (!suspended) {
Services.obs.removeObserver(observer, "cloudsync:user-sync", false);
suspended = true;
}
}.bind(this);
let resume = function () {
if (suspended) {
Services.obs.addObserver(observer, "cloudsync:user-sync", false);
suspended = false;
}
}.bind(this);
let eventSource = new EventSource(eventTypes, suspend, resume);
let registeredAdapters = new Map();
function register (name, opts) {
opts = opts || {};
registeredAdapters.set(name, opts);
}
function unregister (name) {
if (!registeredAdapters.has(name)) {
throw new Error("adapter is not registered: " + name)
}
registeredAdapters.delete(name);
}
function getAdapterNames () {
let result = [];
for (let name of registeredAdapters.keys()) {
result.push(name);
}
return result;
}
function getAdapter (name) {
if (!registeredAdapters.has(name)) {
throw new Error("adapter is not registered: " + name)
}
return registeredAdapters.get(name);
}
function countAdapters () {
return registeredAdapters.size;
}
let observer = {
observe: function (subject, topic, data) {
switch (topic) {
case "cloudsync:user-sync":
eventSource.emit("sync");
break;
}
}
};
this.addEventListener = eventSource.addEventListener;
this.removeEventListener = eventSource.removeEventListener;
this.register = register.bind(this);
this.get = getAdapter.bind(this);
this.unregister = unregister.bind(this);
this.__defineGetter__("names", getAdapterNames);
this.__defineGetter__("count", countAdapters);
};
Adapters.prototype = {
};

@ -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,
};

@ -1,105 +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 = ["FolderCache"];
// Cache for bookmarks folder heirarchy.
var FolderCache = function () {
this.cache = new Map();
}
FolderCache.prototype = {
has: function (id) {
return this.cache.has(id);
},
insert: function (id, parentId) {
if (this.cache.has(id)) {
return;
}
if (parentId && !(this.cache.has(parentId))) {
throw new Error("insert :: parentId not found in cache: " + parentId);
}
this.cache.set(id, {
parent: parentId || null,
children: new Set(),
});
if (parentId) {
this.cache.get(parentId).children.add(id);
}
},
remove: function (id) {
if (!(this.cache.has(id))) {
throw new Error("remote :: id not found in cache: " + id);
}
let parentId = this.cache.get(id).parent;
if (parentId) {
this.cache.get(parentId).children.delete(id);
}
for (let child of this.cache.get(id).children) {
this.cache.get(child).parent = null;
}
this.cache.delete(id);
},
setParent: function (id, parentId) {
if (!(this.cache.has(id))) {
throw new Error("setParent :: id not found in cache: " + id);
}
if (parentId && !(this.cache.has(parentId))) {
throw new Error("setParent :: parentId not found in cache: " + parentId);
}
let oldParent = this.cache.get(id).parent;
if (oldParent) {
this.cache.get(oldParent).children.delete(id);
}
this.cache.get(id).parent = parentId;
this.cache.get(parentId).children.add(id);
return true;
},
getParent: function (id) {
if (this.cache.has(id)) {
return this.cache.get(id).parent;
}
throw new Error("getParent :: id not found in cache: " + id);
},
getChildren: function (id) {
if (this.cache.has(id)) {
return this.cache.get(id).children;
}
throw new Error("getChildren :: id not found in cache: " + id);
},
setChildren: function (id, children) {
for (let child of children) {
if (!this.cache.has(child)) {
this.insert(child, id);
} else {
this.setParent(child, id);
}
}
},
dump: function () {
dump("FolderCache: " + JSON.stringify(this.cache) + "\n");
},
};
this.FolderCache = FolderCache;

@ -1,65 +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/. */
this.EXPORTED_SYMBOLS = ["EventSource"];
Components.utils.import("resource://services-common/utils.js");
var EventSource = function (types, suspendFunc, resumeFunc) {
this.listeners = new Map();
for (let type of types) {
this.listeners.set(type, new Set());
}
this.suspend = suspendFunc || function () {};
this.resume = resumeFunc || function () {};
this.addEventListener = this.addEventListener.bind(this);
this.removeEventListener = this.removeEventListener.bind(this);
};
EventSource.prototype = {
addEventListener: function (type, listener) {
if (!this.listeners.has(type)) {
return;
}
this.listeners.get(type).add(listener);
this.resume();
},
removeEventListener: function (type, listener) {
if (!this.listeners.has(type)) {
return;
}
this.listeners.get(type).delete(listener);
if (!this.hasListeners()) {
this.suspend();
}
},
hasListeners: function () {
for (let l of this.listeners.values()) {
if (l.size > 0) {
return true;
}
}
return false;
},
emit: function (type, arg) {
if (!this.listeners.has(type)) {
return;
}
CommonUtils.nextTick(
function () {
for (let listener of this.listeners.get(type)) {
listener.call(undefined, arg);
}
},
this
);
},
};
this.EventSource = EventSource;

@ -1,87 +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 = ["Local"];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-common/stringbundle.js");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-crypto/utils.js");
Cu.import("resource://gre/modules/Preferences.jsm");
function lazyStrings(name) {
let bundle = "chrome://weave/locale/services/" + name + ".properties";
return () => new StringBundle(bundle);
}
this.Str = {};
XPCOMUtils.defineLazyGetter(Str, "errors", lazyStrings("errors"));
XPCOMUtils.defineLazyGetter(Str, "sync", lazyStrings("sync"));
function makeGUID() {
return CommonUtils.encodeBase64URL(CryptoUtils.generateRandomBytes(9));
}
this.Local = function () {
let prefs = new Preferences("services.cloudsync.");
this.__defineGetter__("prefs", function () {
return prefs;
});
};
Local.prototype = {
get id() {
let clientId = this.prefs.get("client.GUID", "");
return clientId == "" ? this.id = makeGUID(): clientId;
},
set id(value) {
this.prefs.set("client.GUID", value);
},
get name() {
let clientName = this.prefs.get("client.name", "");
if (clientName != "") {
return clientName;
}
// Generate a client name if we don't have a useful one yet
let env = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment);
let user = env.get("USER") || env.get("USERNAME");
let appName;
let brand = new StringBundle("chrome://branding/locale/brand.properties");
let brandName = brand.get("brandShortName");
try {
let syncStrings = new StringBundle("chrome://browser/locale/sync.properties");
appName = syncStrings.getFormattedString("sync.defaultAccountApplication", [brandName]);
} catch (ex) {
}
appName = appName || brandName;
let system =
// 'device' is defined on unix systems
Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("device") ||
// hostname of the system, usually assigned by the user or admin
Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2).get("host") ||
// fall back on ua info string
Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).oscpu;
return this.name = Str.sync.get("client.name2", [user, appName, system]);
},
set name(value) {
this.prefs.set("client.name", value);
},
};

@ -1,375 +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 = ["PlacesWrapper"];
const {interfaces: Ci, utils: Cu} = Components;
const REASON_ERROR = Ci.mozIStorageStatementCallback.REASON_ERROR;
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource:///modules/PlacesUIUtils.jsm");
Cu.import("resource://services-common/utils.js");
var PlacesQueries = function () {
}
PlacesQueries.prototype = {
cachedStmts: {},