summaryrefslogtreecommitdiff
path: root/components/weave/src/engines/bookmarks.js
diff options
context:
space:
mode:
Diffstat (limited to 'components/weave/src/engines/bookmarks.js')
-rw-r--r--components/weave/src/engines/bookmarks.js1542
1 files changed, 0 insertions, 1542 deletions
diff --git a/components/weave/src/engines/bookmarks.js b/components/weave/src/engines/bookmarks.js
deleted file mode 100644
index 61e771134..000000000
--- a/components/weave/src/engines/bookmarks.js
+++ /dev/null
@@ -1,1542 +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 = ['BookmarksEngine', "PlacesItem", "Bookmark",
- "BookmarkFolder", "BookmarkQuery",
- "Livemark", "BookmarkSeparator"];
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Async.jsm");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/engines.js");
-Cu.import("resource://services-sync/record.js");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/PlacesBackups.jsm");
-
-const ALLBOOKMARKS_ANNO = "AllBookmarks";
-const DESCRIPTION_ANNO = "bookmarkProperties/description";
-const SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
-const MOBILEROOT_ANNO = "mobile/bookmarksRoot";
-const MOBILE_ANNO = "MobileBookmarks";
-const EXCLUDEBACKUP_ANNO = "places/excludeFromBackup";
-const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
-const PARENT_ANNO = "sync/parent";
-const ORGANIZERQUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
-const ANNOS_TO_TRACK = [DESCRIPTION_ANNO, SIDEBAR_ANNO,
- PlacesUtils.LMANNO_FEEDURI, PlacesUtils.LMANNO_SITEURI];
-
-const SERVICE_NOT_SUPPORTED = "Service not supported on this platform";
-const FOLDER_SORTINDEX = 1000000;
-
-this.PlacesItem = function PlacesItem(collection, id, type) {
- CryptoWrapper.call(this, collection, id);
- this.type = type || "item";
-}
-PlacesItem.prototype = {
- decrypt: function PlacesItem_decrypt(keyBundle) {
- // Do the normal CryptoWrapper decrypt, but change types before returning
- let clear = CryptoWrapper.prototype.decrypt.call(this, keyBundle);
-
- // Convert the abstract places item to the actual object type
- if (!this.deleted)
- this.__proto__ = this.getTypeObject(this.type).prototype;
-
- return clear;
- },
-
- getTypeObject: function PlacesItem_getTypeObject(type) {
- switch (type) {
- case "bookmark":
- case "microsummary":
- return Bookmark;
- case "query":
- return BookmarkQuery;
- case "folder":
- return BookmarkFolder;
- case "livemark":
- return Livemark;
- case "separator":
- return BookmarkSeparator;
- case "item":
- return PlacesItem;
- }
- throw "Unknown places item object type: " + type;
- },
-
- __proto__: CryptoWrapper.prototype,
- _logName: "Sync.Record.PlacesItem",
-};
-
-Utils.deferGetSet(PlacesItem,
- "cleartext",
- ["hasDupe", "parentid", "parentName", "type"]);
-
-this.Bookmark = function Bookmark(collection, id, type) {
- PlacesItem.call(this, collection, id, type || "bookmark");
-}
-Bookmark.prototype = {
- __proto__: PlacesItem.prototype,
- _logName: "Sync.Record.Bookmark",
-};
-
-Utils.deferGetSet(Bookmark,
- "cleartext",
- ["title", "bmkUri", "description",
- "loadInSidebar", "tags", "keyword"]);
-
-this.BookmarkQuery = function BookmarkQuery(collection, id) {
- Bookmark.call(this, collection, id, "query");
-}
-BookmarkQuery.prototype = {
- __proto__: Bookmark.prototype,
- _logName: "Sync.Record.BookmarkQuery",
-};
-
-Utils.deferGetSet(BookmarkQuery,
- "cleartext",
- ["folderName", "queryId"]);
-
-this.BookmarkFolder = function BookmarkFolder(collection, id, type) {
- PlacesItem.call(this, collection, id, type || "folder");
-}
-BookmarkFolder.prototype = {
- __proto__: PlacesItem.prototype,
- _logName: "Sync.Record.Folder",
-};
-
-Utils.deferGetSet(BookmarkFolder, "cleartext", ["description", "title",
- "children"]);
-
-this.Livemark = function Livemark(collection, id) {
- BookmarkFolder.call(this, collection, id, "livemark");
-}
-Livemark.prototype = {
- __proto__: BookmarkFolder.prototype,
- _logName: "Sync.Record.Livemark",
-};
-
-Utils.deferGetSet(Livemark, "cleartext", ["siteUri", "feedUri"]);
-
-this.BookmarkSeparator = function BookmarkSeparator(collection, id) {
- PlacesItem.call(this, collection, id, "separator");
-}
-BookmarkSeparator.prototype = {
- __proto__: PlacesItem.prototype,
- _logName: "Sync.Record.Separator",
-};
-
-Utils.deferGetSet(BookmarkSeparator, "cleartext", "pos");
-
-
-let kSpecialIds = {
-
- // Special IDs. Note that mobile can attempt to create a record on
- // dereference; special accessors are provided to prevent recursion within
- // observers.
- guids: ["menu", "places", "tags", "toolbar", "unfiled", "mobile"],
-
- // Create the special mobile folder to store mobile bookmarks.
- createMobileRoot: function createMobileRoot() {
- let root = PlacesUtils.placesRootId;
- let mRoot = PlacesUtils.bookmarks.createFolder(root, "mobile", -1);
- PlacesUtils.annotations.setItemAnnotation(
- mRoot, MOBILEROOT_ANNO, 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
- PlacesUtils.annotations.setItemAnnotation(
- mRoot, EXCLUDEBACKUP_ANNO, 1, 0, PlacesUtils.annotations.EXPIRE_NEVER);
- return mRoot;
- },
-
- findMobileRoot: function findMobileRoot(create) {
- // Use the (one) mobile root if it already exists.
- let root = PlacesUtils.annotations.getItemsWithAnnotation(MOBILEROOT_ANNO, {});
- if (root.length != 0)
- return root[0];
-
- if (create)
- return this.createMobileRoot();
-
- return null;
- },
-
- // Accessors for IDs.
- isSpecialGUID: function isSpecialGUID(g) {
- return this.guids.indexOf(g) != -1;
- },
-
- specialIdForGUID: function specialIdForGUID(guid, create) {
- if (guid == "mobile") {
- return this.findMobileRoot(create);
- }
- return this[guid];
- },
-
- // Don't bother creating mobile: if it doesn't exist, this ID can't be it!
- specialGUIDForId: function specialGUIDForId(id) {
- for each (let guid in this.guids)
- if (this.specialIdForGUID(guid, false) == id)
- return guid;
- return null;
- },
-
- get menu() {
- return PlacesUtils.bookmarksMenuFolderId;
- },
- get places() {
- return PlacesUtils.placesRootId;
- },
- get tags() {
- return PlacesUtils.tagsFolderId;
- },
- get toolbar() {
- return PlacesUtils.toolbarFolderId;
- },
- get unfiled() {
- return PlacesUtils.unfiledBookmarksFolderId;
- },
- get mobile() {
- return this.findMobileRoot(true);
- },
-};
-
-this.BookmarksEngine = function BookmarksEngine(service) {
- SyncEngine.call(this, "Bookmarks", service);
-}
-BookmarksEngine.prototype = {
- __proto__: SyncEngine.prototype,
- _recordObj: PlacesItem,
- _storeObj: BookmarksStore,
- _trackerObj: BookmarksTracker,
- version: 2,
- _defaultSort: "index",
-
- syncPriority: 4,
-
- _sync: function _sync() {
- let engine = this;
- let batchEx = null;
-
- // Try running sync in batch mode
- PlacesUtils.bookmarks.runInBatchMode({
- runBatched: function wrappedSync() {
- try {
- SyncEngine.prototype._sync.call(engine);
- }
- catch(ex) {
- batchEx = ex;
- }
- }
- }, null);
-
- // Expose the exception if something inside the batch failed
- if (batchEx != null) {
- throw batchEx;
- }
- },
-
- _guidMapFailed: false,
- _buildGUIDMap: function _buildGUIDMap() {
- let guidMap = {};
- for (let guid in this._store.getAllIDs()) {
- // Figure out with which key to store the mapping.
- let key;
- let id = this._store.idForGUID(guid);
- switch (PlacesUtils.bookmarks.getItemType(id)) {
- case PlacesUtils.bookmarks.TYPE_BOOKMARK:
-
- // Smart bookmarks map to their annotation value.
- let queryId;
- try {
- queryId = PlacesUtils.annotations.getItemAnnotation(
- id, SMART_BOOKMARKS_ANNO);
- } catch(ex) {}
-
- if (queryId)
- key = "q" + queryId;
- else
- key = "b" + PlacesUtils.bookmarks.getBookmarkURI(id).spec + ":" +
- PlacesUtils.bookmarks.getItemTitle(id);
- break;
- case PlacesUtils.bookmarks.TYPE_FOLDER:
- key = "f" + PlacesUtils.bookmarks.getItemTitle(id);
- break;
- case PlacesUtils.bookmarks.TYPE_SEPARATOR:
- key = "s" + PlacesUtils.bookmarks.getItemIndex(id);
- break;
- default:
- continue;
- }
-
- // The mapping is on a per parent-folder-name basis.
- let parent = PlacesUtils.bookmarks.getFolderIdForItem(id);
- if (parent <= 0)
- continue;
-
- let parentName = PlacesUtils.bookmarks.getItemTitle(parent);
- if (guidMap[parentName] == null)
- guidMap[parentName] = {};
-
- // If the entry already exists, remember that there are explicit dupes.
- let entry = new String(guid);
- entry.hasDupe = guidMap[parentName][key] != null;
-
- // Remember this item's GUID for its parent-name/key pair.
- guidMap[parentName][key] = entry;
- this._log.trace("Mapped: " + [parentName, key, entry, entry.hasDupe]);
- }
-
- return guidMap;
- },
-
- // Helper function to get a dupe GUID for an item.
- _mapDupe: function _mapDupe(item) {
- // Figure out if we have something to key with.
- let key;
- let altKey;
- switch (item.type) {
- case "query":
- // Prior to Bug 610501, records didn't carry their Smart Bookmark
- // anno, so we won't be able to dupe them correctly. This altKey
- // hack should get them to dupe correctly.
- if (item.queryId) {
- key = "q" + item.queryId;
- altKey = "b" + item.bmkUri + ":" + item.title;
- break;
- }
- // No queryID? Fall through to the regular bookmark case.
- case "bookmark":
- case "microsummary":
- key = "b" + item.bmkUri + ":" + item.title;
- break;
- case "folder":
- case "livemark":
- key = "f" + item.title;
- break;
- case "separator":
- key = "s" + item.pos;
- break;
- default:
- return;
- }
-
- // Figure out if we have a map to use!
- // This will throw in some circumstances. That's fine.
- let guidMap = this._guidMap;
-
- // Give the GUID if we have the matching pair.
- this._log.trace("Finding mapping: " + item.parentName + ", " + key);
- let parent = guidMap[item.parentName];
-
- if (!parent) {
- this._log.trace("No parent => no dupe.");
- return undefined;
- }
-
- let dupe = parent[key];
-
- if (dupe) {
- this._log.trace("Mapped dupe: " + dupe);
- return dupe;
- }
-
- if (altKey) {
- dupe = parent[altKey];
- if (dupe) {
- this._log.trace("Mapped dupe using altKey " + altKey + ": " + dupe);
- return dupe;
- }
- }
-
- this._log.trace("No dupe found for key " + key + "/" + altKey + ".");
- return undefined;
- },
-
- _syncStartup: function _syncStart() {
- SyncEngine.prototype._syncStartup.call(this);
-
- let cb = Async.makeSpinningCallback();
- Task.spawn(function() {
- // For first-syncs, make a backup for the user to restore
- if (this.lastSync == 0) {
- this._log.debug("Bookmarks backup starting.");
- yield PlacesBackups.create(null, true);
- this._log.debug("Bookmarks backup done.");
- }
- }.bind(this)).then(
- cb, ex => {
- // Failure to create a backup is somewhat bad, but probably not bad
- // enough to prevent syncing of bookmarks - so just log the error and
- // continue.
- this._log.warn("Got exception backing up bookmarks, but continuing with sync.", ex);
- cb();
- }
- );
-
- cb.wait();
-
- this.__defineGetter__("_guidMap", function() {
- // Create a mapping of folder titles and separator positions to GUID.
- // We do this lazily so that we don't do any work unless we reconcile
- // incoming items.
- let guidMap;
- try {
- guidMap = this._buildGUIDMap();
- } catch (ex) {
- this._log.warn("Got exception building GUID map." +
- " Skipping all other incoming items.", ex);
- throw {code: Engine.prototype.eEngineAbortApplyIncoming,
- cause: ex};
- }
- delete this._guidMap;
- return this._guidMap = guidMap;
- });
-
- this._store._childrenToOrder = {};
- },
-
- _processIncoming: function (newitems) {
- try {
- SyncEngine.prototype._processIncoming.call(this, newitems);
- } finally {
- // Reorder children.
- this._tracker.ignoreAll = true;
- this._store._orderChildren();
- this._tracker.ignoreAll = false;
- delete this._store._childrenToOrder;
- }
- },
-
- _syncFinish: function _syncFinish() {
- SyncEngine.prototype._syncFinish.call(this);
- this._tracker._ensureMobileQuery();
- },
-
- _syncCleanup: function _syncCleanup() {
- SyncEngine.prototype._syncCleanup.call(this);
- delete this._guidMap;
- },
-
- _createRecord: function _createRecord(id) {
- // Create the record as usual, but mark it as having dupes if necessary.
- let record = SyncEngine.prototype._createRecord.call(this, id);
- let entry = this._mapDupe(record);
- if (entry != null && entry.hasDupe) {
- record.hasDupe = true;
- }
- return record;
- },
-
- _findDupe: function _findDupe(item) {
- this._log.trace("Finding dupe for " + item.id +
- " (already duped: " + item.hasDupe + ").");
-
- // Don't bother finding a dupe if the incoming item has duplicates.
- if (item.hasDupe) {
- this._log.trace(item.id + " already a dupe: not finding one.");
- return;
- }
- let mapped = this._mapDupe(item);
- this._log.debug(item.id + " mapped to " + mapped);
- return mapped;
- }
-};
-
-function BookmarksStore(name, engine) {
- Store.call(this, name, engine);
-
- // Explicitly nullify our references to our cached services so we don't leak
- Svc.Obs.add("places-shutdown", function() {
- for each (let [query, stmt] in Iterator(this._stmts)) {
- stmt.finalize();
- }
- this._stmts = {};
- }, this);
-}
-BookmarksStore.prototype = {
- __proto__: Store.prototype,
-
- itemExists: function BStore_itemExists(id) {
- return this.idForGUID(id, true) > 0;
- },
-
- /*
- * If the record is a tag query, rewrite it to refer to the local tag ID.
- *
- * Otherwise, just return.
- */
- preprocessTagQuery: function preprocessTagQuery(record) {
- if (record.type != "query" ||
- record.bmkUri == null ||
- !record.folderName)
- return;
-
- // Yes, this works without chopping off the "place:" prefix.
- let uri = record.bmkUri
- let queriesRef = {};
- let queryCountRef = {};
- let optionsRef = {};
- PlacesUtils.history.queryStringToQueries(uri, queriesRef, queryCountRef,
- optionsRef);
-
- // We only process tag URIs.
- if (optionsRef.value.resultType != optionsRef.value.RESULTS_AS_TAG_CONTENTS)
- return;
-
- // Tag something to ensure that the tag exists.
- let tag = record.folderName;
- let dummyURI = Utils.makeURI("about:weave#BStore_preprocess");
- PlacesUtils.tagging.tagURI(dummyURI, [tag]);
-
- // Look for the id of the tag, which might just have been added.
- let tags = this._getNode(PlacesUtils.tagsFolderId);
- if (!(tags instanceof Ci.nsINavHistoryQueryResultNode)) {
- this._log.debug("tags isn't an nsINavHistoryQueryResultNode; aborting.");
- return;
- }
-
- tags.containerOpen = true;
- try {
- for (let i = 0; i < tags.childCount; i++) {
- let child = tags.getChild(i);
- if (child.title == tag) {
- // Found the tag, so fix up the query to use the right id.
- this._log.debug("Tag query folder: " + tag + " = " + child.itemId);
-
- this._log.trace("Replacing folders in: " + uri);
- for each (let q in queriesRef.value)
- q.setFolders([child.itemId], 1);
-
- record.bmkUri = PlacesUtils.history.queriesToQueryString(
- queriesRef.value, queryCountRef.value, optionsRef.value);
- return;
- }
- }
- }
- finally {
- tags.containerOpen = false;
- }
- },
-
- applyIncoming: function BStore_applyIncoming(record) {
- this._log.debug("Applying record " + record.id);
- let isSpecial = record.id in kSpecialIds;
-
- if (record.deleted) {
- if (isSpecial) {
- this._log.warn("Ignoring deletion for special record " + record.id);
- return;
- }
-
- // Don't bother with pre and post-processing for deletions.
- Store.prototype.applyIncoming.call(this, record);
- return;
- }
-
- // For special folders we're only interested in child ordering.
- if (isSpecial && record.children) {
- this._log.debug("Processing special node: " + record.id);
- // Reorder children later
- this._childrenToOrder[record.id] = record.children;
- return;
- }
-
- // Skip malformed records. (Bug 806460.)
- if (record.type == "query" &&
- !record.bmkUri) {
- this._log.warn("Skipping malformed query bookmark: " + record.id);
- return;
- }
-
- // Preprocess the record before doing the normal apply.
- this.preprocessTagQuery(record);
-
- // Figure out the local id of the parent GUID if available
- let parentGUID = record.parentid;
- if (!parentGUID) {
- throw "Record " + record.id + " has invalid parentid: " + parentGUID;
- }
- this._log.debug("Local parent is " + parentGUID);
-
- let parentId = this.idForGUID(parentGUID);
- if (parentId > 0) {
- // Save the parent id for modifying the bookmark later
- record._parent = parentId;
- record._orphan = false;
- this._log.debug("Record " + record.id + " is not an orphan.");
- } else {
- this._log.trace("Record " + record.id +
- " is an orphan: could not find parent " + parentGUID);
- record._orphan = true;
- }
-
- // Do the normal processing of incoming records
- Store.prototype.applyIncoming.call(this, record);
-
- // Do some post-processing if we have an item
- let itemId = this.idForGUID(record.id);
- if (itemId > 0) {
- // Move any children that are looking for this folder as a parent
- if (record.type == "folder") {
- this._reparentOrphans(itemId);
- // Reorder children later
- if (record.children)
- this._childrenToOrder[record.id] = record.children;
- }
-
- // Create an annotation to remember that it needs reparenting.
- if (record._orphan) {
- PlacesUtils.annotations.setItemAnnotation(
- itemId, PARENT_ANNO, parentGUID, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
- }
- },
-
- /**
- * Find all ids of items that have a given value for an annotation
- */
- _findAnnoItems: function BStore__findAnnoItems(anno, val) {
- return PlacesUtils.annotations.getItemsWithAnnotation(anno, {})
- .filter(function(id) {
- return PlacesUtils.annotations.getItemAnnotation(id, anno) == val;
- });
- },
-
- /**
- * For the provided parent item, attach its children to it
- */
- _reparentOrphans: function _reparentOrphans(parentId) {
- // Find orphans and reunite with this folder parent
- let parentGUID = this.GUIDForId(parentId);
- let orphans = this._findAnnoItems(PARENT_ANNO, parentGUID);
-
- this._log.debug("Reparenting orphans " + orphans + " to " + parentId);
- orphans.forEach(function(orphan) {
- // Move the orphan to the parent and drop the missing parent annotation
- if (this._reparentItem(orphan, parentId)) {
- PlacesUtils.annotations.removeItemAnnotation(orphan, PARENT_ANNO);
- }
- }, this);
- },
-
- _reparentItem: function _reparentItem(itemId, parentId) {
- this._log.trace("Attempting to move item " + itemId + " to new parent " +
- parentId);
- try {
- if (parentId > 0) {
- PlacesUtils.bookmarks.moveItem(itemId, parentId,
- PlacesUtils.bookmarks.DEFAULT_INDEX);
- return true;
- }
- } catch(ex) {
- this._log.debug("Failed to reparent item. ", ex);
- }
- return false;
- },
-
- // Turn a record's nsINavBookmarksService constant and other attributes into
- // a granular type for comparison.
- _recordType: function _recordType(itemId) {
- let bms = PlacesUtils.bookmarks;
- let type = bms.getItemType(itemId);
-
- switch (type) {
- case bms.TYPE_FOLDER:
- if (PlacesUtils.annotations
- .itemHasAnnotation(itemId, PlacesUtils.LMANNO_FEEDURI)) {
- return "livemark";
- }
- return "folder";
-
- case bms.TYPE_BOOKMARK:
- let bmkUri = bms.getBookmarkURI(itemId).spec;
- if (bmkUri.indexOf("place:") == 0) {
- return "query";
- }
- return "bookmark";
-
- case bms.TYPE_SEPARATOR:
- return "separator";
-
- default:
- return null;
- }
- },
-
- create: function BStore_create(record) {
- // Default to unfiled if we don't have the parent yet.
-
- // Valid parent IDs are all positive integers. Other values -- undefined,
- // null, -1 -- all compare false for > 0, so this catches them all. We
- // don't just use <= without the !, because undefined and null compare
- // false for that, too!
- if (!(record._parent > 0)) {
- this._log.debug("Parent is " + record._parent + "; reparenting to unfiled.");
- record._parent = kSpecialIds.unfiled;
- }
-
- let newId;
- switch (record.type) {
- case "bookmark":
- case "query":
- case "microsummary": {
- let uri = Utils.makeURI(record.bmkUri);
- newId = PlacesUtils.bookmarks.insertBookmark(
- record._parent, uri, PlacesUtils.bookmarks.DEFAULT_INDEX, record.title);
- this._log.debug("created bookmark " + newId + " under " + record._parent
- + " as " + record.title + " " + record.bmkUri);
-
- // Smart bookmark annotations are strings.
- if (record.queryId) {
- PlacesUtils.annotations.setItemAnnotation(
- newId, SMART_BOOKMARKS_ANNO, record.queryId, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
-
- if (Array.isArray(record.tags)) {
- this._tagURI(uri, record.tags);
- }
- PlacesUtils.bookmarks.setKeywordForBookmark(newId, record.keyword);
- if (record.description) {
- PlacesUtils.annotations.setItemAnnotation(
- newId, DESCRIPTION_ANNO, record.description, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
-
- if (record.loadInSidebar) {
- PlacesUtils.annotations.setItemAnnotation(
- newId, SIDEBAR_ANNO, true, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
-
- } break;
- case "folder":
- newId = PlacesUtils.bookmarks.createFolder(
- record._parent, record.title, PlacesUtils.bookmarks.DEFAULT_INDEX);
- this._log.debug("created folder " + newId + " under " + record._parent
- + " as " + record.title);
-
- if (record.description) {
- PlacesUtils.annotations.setItemAnnotation(
- newId, DESCRIPTION_ANNO, record.description, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
-
- // record.children will be dealt with in _orderChildren.
- break;
- case "livemark":
- let siteURI = null;
- if (!record.feedUri) {
- this._log.debug("No feed URI: skipping livemark record " + record.id);
- return;
- }
- if (PlacesUtils.annotations
- .itemHasAnnotation(record._parent, PlacesUtils.LMANNO_FEEDURI)) {
- this._log.debug("Invalid parent: skipping livemark record " + record.id);
- return;
- }
-
- if (record.siteUri != null)
- siteURI = Utils.makeURI(record.siteUri);
-
- // Until this engine can handle asynchronous error reporting, we need to
- // detect errors on creation synchronously.
- let spinningCb = Async.makeSpinningCallback();
-
- let livemarkObj = {title: record.title,
- parentId: record._parent,
- index: PlacesUtils.bookmarks.DEFAULT_INDEX,
- feedURI: Utils.makeURI(record.feedUri),
- siteURI: siteURI,
- guid: record.id};
- PlacesUtils.livemarks.addLivemark(livemarkObj).then(
- aLivemark => { spinningCb(null, [Components.results.NS_OK, aLivemark]) },
- () => { spinningCb(null, [Components.results.NS_ERROR_UNEXPECTED, aLivemark]) }
- );
-
- let [status, livemark] = spinningCb.wait();
- if (!Components.isSuccessCode(status)) {
- throw status;
- }
-
- this._log.debug("Created livemark " + livemark.id + " under " +
- livemark.parentId + " as " + livemark.title +
- ", " + livemark.siteURI.spec + ", " +
- livemark.feedURI.spec + ", GUID " +
- livemark.guid);
- break;
- case "separator":
- newId = PlacesUtils.bookmarks.insertSeparator(
- record._parent, PlacesUtils.bookmarks.DEFAULT_INDEX);
- this._log.debug("created separator " + newId + " under " + record._parent);
- break;
- case "item":
- this._log.debug(" -> got a generic places item.. do nothing?");
- return;
- default:
- this._log.error("_create: Unknown item type: " + record.type);
- return;
- }
-
- if (newId) {
- // Livemarks can set the GUID through the API, so there's no need to
- // do that here.
- this._log.trace("Setting GUID of new item " + newId + " to " + record.id);
- this._setGUID(newId, record.id);
- }
- },
-
- // Factored out of `remove` to avoid redundant DB queries when the Places ID
- // is already known.
- removeById: function removeById(itemId, guid) {
- let type = PlacesUtils.bookmarks.getItemType(itemId);
-
- switch (type) {
- case PlacesUtils.bookmarks.TYPE_BOOKMARK:
- this._log.debug(" -> removing bookmark " + guid);
- PlacesUtils.bookmarks.removeItem(itemId);
- break;
- case PlacesUtils.bookmarks.TYPE_FOLDER:
- this._log.debug(" -> removing folder " + guid);
- PlacesUtils.bookmarks.removeItem(itemId);
- break;
- case PlacesUtils.bookmarks.TYPE_SEPARATOR:
- this._log.debug(" -> removing separator " + guid);
- PlacesUtils.bookmarks.removeItem(itemId);
- break;
- default:
- this._log.error("remove: Unknown item type: " + type);
- break;
- }
- },
-
- remove: function BStore_remove(record) {
- if (kSpecialIds.isSpecialGUID(record.id)) {
- this._log.warn("Refusing to remove special folder " + record.id);
- return;
- }
-
- let itemId = this.idForGUID(record.id);
- if (itemId <= 0) {
- this._log.debug("Item " + record.id + " already removed");
- return;
- }
- this.removeById(itemId, record.id);
- },
-
- _taggableTypes: ["bookmark", "microsummary", "query"],
- isTaggable: function isTaggable(recordType) {
- return this._taggableTypes.indexOf(recordType) != -1;
- },
-
- update: function BStore_update(record) {
- let itemId = this.idForGUID(record.id);
-
- if (itemId <= 0) {
- this._log.debug("Skipping update for unknown item: " + record.id);
- return;
- }
-
- // Two items are the same type if they have the same ItemType in Places,
- // and also share some key characteristics (e.g., both being livemarks).
- // We figure this out by examining the item to find the equivalent granular
- // (string) type.
- // If they're not the same type, we can't just update attributes. Delete
- // then recreate the record instead.
- let localItemType = this._recordType(itemId);
- let remoteRecordType = record.type;
- this._log.trace("Local type: " + localItemType + ". " +
- "Remote type: " + remoteRecordType + ".");
-
- if (localItemType != remoteRecordType) {
- this._log.debug("Local record and remote record differ in type. " +
- "Deleting and recreating.");
- this.removeById(itemId, record.id);
- this.create(record);
- return;
- }
-
- this._log.trace("Updating " + record.id + " (" + itemId + ")");
-
- // Move the bookmark to a new parent or new position if necessary
- if (record._parent > 0 &&
- PlacesUtils.bookmarks.getFolderIdForItem(itemId) != record._parent) {
- this._reparentItem(itemId, record._parent);
- }
-
- for (let [key, val] in Iterator(record.cleartext)) {
- switch (key) {
- case "title":
- PlacesUtils.bookmarks.setItemTitle(itemId, val);
- break;
- case "bmkUri":
- PlacesUtils.bookmarks.changeBookmarkURI(itemId, Utils.makeURI(val));
- break;
- case "tags":
- if (Array.isArray(val)) {
- if (this.isTaggable(remoteRecordType)) {
- this._tagID(itemId, val);
- } else {
- this._log.debug("Remote record type is invalid for tags: " + remoteRecordType);
- }
- }
- break;
- case "keyword":
- PlacesUtils.bookmarks.setKeywordForBookmark(itemId, val);
- break;
- case "description":
- if (val) {
- PlacesUtils.annotations.setItemAnnotation(
- itemId, DESCRIPTION_ANNO, val, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- } else {
- PlacesUtils.annotations.removeItemAnnotation(itemId, DESCRIPTION_ANNO);
- }
- break;
- case "loadInSidebar":
- if (val) {
- PlacesUtils.annotations.setItemAnnotation(
- itemId, SIDEBAR_ANNO, true, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- } else {
- PlacesUtils.annotations.removeItemAnnotation(itemId, SIDEBAR_ANNO);
- }
- break;
- case "queryId":
- PlacesUtils.annotations.setItemAnnotation(
- itemId, SMART_BOOKMARKS_ANNO, val, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- break;
- }
- }
- },
-
- _orderChildren: function _orderChildren() {
- for (let [guid, children] in Iterator(this._childrenToOrder)) {
- // Reorder children according to the GUID list. Gracefully deal
- // with missing items, e.g. locally deleted.
- let delta = 0;
- let parent = null;
- for (let idx = 0; idx < children.length; idx++) {
- let itemid = this.idForGUID(children[idx]);
- if (itemid == -1) {
- delta += 1;
- this._log.trace("Could not locate record " + children[idx]);
- continue;
- }
- try {
- // This code path could be optimized by caching the parent earlier.
- // Doing so should take in count any edge case due to reparenting
- // or parent invalidations though.
- if (!parent) {
- parent = PlacesUtils.bookmarks.getFolderIdForItem(itemid);
- }
- PlacesUtils.bookmarks.moveItem(itemid, parent, idx - delta);
- } catch (ex) {
- this._log.debug("Could not move item " + children[idx] + ": " + ex);
- }
- }
- }
- },
-
- changeItemID: function BStore_changeItemID(oldID, newID) {
- this._log.debug("Changing GUID " + oldID + " to " + newID);
-
- // Make sure there's an item to change GUIDs
- let itemId = this.idForGUID(oldID);
- if (itemId <= 0)
- return;
-
- this._setGUID(itemId, newID);
- },
-
- _getNode: function BStore__getNode(folder) {
- let query = PlacesUtils.history.getNewQuery();
- query.setFolders([folder], 1);
- return PlacesUtils.history.executeQuery(
- query, PlacesUtils.history.getNewQueryOptions()).root;
- },
-
- _getTags: function BStore__getTags(uri) {
- try {
- if (typeof(uri) == "string")
- uri = Utils.makeURI(uri);
- } catch(e) {
- this._log.warn("Could not parse URI \"" + uri + "\": " + e);
- }
- return PlacesUtils.tagging.getTagsForURI(uri, {});
- },
-
- _getDescription: function BStore__getDescription(id) {
- try {
- return PlacesUtils.annotations.getItemAnnotation(id, DESCRIPTION_ANNO);
- } catch (e) {
- return null;
- }
- },
-
- _isLoadInSidebar: function BStore__isLoadInSidebar(id) {
- return PlacesUtils.annotations.itemHasAnnotation(id, SIDEBAR_ANNO);
- },
-
- get _childGUIDsStm() {
- return this._getStmt(
- "SELECT id AS item_id, guid " +
- "FROM moz_bookmarks " +
- "WHERE parent = :parent " +
- "ORDER BY position");
- },
- _childGUIDsCols: ["item_id", "guid"],
-
- _getChildGUIDsForId: function _getChildGUIDsForId(itemid) {
- let stmt = this._childGUIDsStm;
- stmt.params.parent = itemid;
- let rows = Async.querySpinningly(stmt, this._childGUIDsCols);
- return rows.map(function (row) {
- if (row.guid) {
- return row.guid;
- }
- // A GUID hasn't been assigned to this item yet, do this now.
- return this.GUIDForId(row.item_id);
- }, this);
- },
-
- // Create a record starting from the weave id (places guid)
- createRecord: function createRecord(id, collection) {
- let placeId = this.idForGUID(id);
- let record;
- if (placeId <= 0) { // deleted item
- record = new PlacesItem(collection, id);
- record.deleted = true;
- return record;
- }
-
- let parent = PlacesUtils.bookmarks.getFolderIdForItem(placeId);
- switch (PlacesUtils.bookmarks.getItemType(placeId)) {
- case PlacesUtils.bookmarks.TYPE_BOOKMARK:
- let bmkUri = PlacesUtils.bookmarks.getBookmarkURI(placeId).spec;
- if (bmkUri.indexOf("place:") == 0) {
- record = new BookmarkQuery(collection, id);
-
- // Get the actual tag name instead of the local itemId
- let folder = bmkUri.match(/[:&]folder=(\d+)/);
- try {
- // There might not be the tag yet when creating on a new client
- if (folder != null) {
- folder = folder[1];
- record.folderName = PlacesUtils.bookmarks.getItemTitle(folder);
- this._log.trace("query id: " + folder + " = " + record.folderName);
- }
- }
- catch(ex) {}
-
- // Persist the Smart Bookmark anno, if found.
- try {
- let anno = PlacesUtils.annotations.getItemAnnotation(placeId, SMART_BOOKMARKS_ANNO);
- if (anno != null) {
- this._log.trace("query anno: " + SMART_BOOKMARKS_ANNO +
- " = " + anno);
- record.queryId = anno;
- }
- }
- catch(ex) {}
- }
- else {
- record = new Bookmark(collection, id);
- }
- record.title = PlacesUtils.bookmarks.getItemTitle(placeId);
-
- record.parentName = PlacesUtils.bookmarks.getItemTitle(parent);
- record.bmkUri = bmkUri;
- record.tags = this._getTags(record.bmkUri);
- record.keyword = PlacesUtils.bookmarks.getKeywordForBookmark(placeId);
- record.description = this._getDescription(placeId);
- record.loadInSidebar = this._isLoadInSidebar(placeId);
- break;
-
- case PlacesUtils.bookmarks.TYPE_FOLDER:
- if (PlacesUtils.annotations
- .itemHasAnnotation(placeId, PlacesUtils.LMANNO_FEEDURI)) {
- record = new Livemark(collection, id);
- let as = PlacesUtils.annotations;
- record.feedUri = as.getItemAnnotation(placeId, PlacesUtils.LMANNO_FEEDURI);
- try {
- record.siteUri = as.getItemAnnotation(placeId, PlacesUtils.LMANNO_SITEURI);
- } catch (ex) {}
- } else {
- record = new BookmarkFolder(collection, id);
- }
-
- if (parent > 0)
- record.parentName = PlacesUtils.bookmarks.getItemTitle(parent);
- record.title = PlacesUtils.bookmarks.getItemTitle(placeId);
- record.description = this._getDescription(placeId);
- record.children = this._getChildGUIDsForId(placeId);
- break;
-
- case PlacesUtils.bookmarks.TYPE_SEPARATOR:
- record = new BookmarkSeparator(collection, id);
- if (parent > 0)
- record.parentName = PlacesUtils.bookmarks.getItemTitle(parent);
- // Create a positioning identifier for the separator, used by _mapDupe
- record.pos = PlacesUtils.bookmarks.getItemIndex(placeId);
- break;
-
- default:
- record = new PlacesItem(collection, id);
- this._log.warn("Unknown item type, cannot serialize: " +
- PlacesUtils.bookmarks.getItemType(placeId));
- }
-
- record.parentid = this.GUIDForId(parent);
- record.sortindex = this._calculateIndex(record);
-
- return record;
- },
-
- _stmts: {},
- _getStmt: function(query) {
- if (query in this._stmts) {
- return this._stmts[query];
- }
-
- this._log.trace("Creating SQL statement: " + query);
- let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
- .DBConnection;
- return this._stmts[query] = db.createAsyncStatement(query);
- },
-
- get _frecencyStm() {
- return this._getStmt(
- "SELECT frecency " +
- "FROM moz_places " +
- "WHERE url = :url " +
- "LIMIT 1");
- },
- _frecencyCols: ["frecency"],
-
- get _setGUIDStm() {
- return this._getStmt(
- "UPDATE moz_bookmarks " +
- "SET guid = :guid " +
- "WHERE id = :item_id");
- },
-
- // Some helper functions to handle GUIDs
- _setGUID: function _setGUID(id, guid) {
- if (!guid)
- guid = Utils.makeGUID();
-
- let stmt = this._setGUIDStm;
- stmt.params.guid = guid;
- stmt.params.item_id = id;
- Async.querySpinningly(stmt);
- return guid;
- },
-
- get _guidForIdStm() {
- return this._getStmt(
- "SELECT guid " +
- "FROM moz_bookmarks " +
- "WHERE id = :item_id");
- },
- _guidForIdCols: ["guid"],
-
- GUIDForId: function GUIDForId(id) {
- let special = kSpecialIds.specialGUIDForId(id);
- if (special)
- return special;
-
- let stmt = this._guidForIdStm;
- stmt.params.item_id = id;
-
- // Use the existing GUID if it exists
- let result = Async.querySpinningly(stmt, this._guidForIdCols)[0];
- if (result && result.guid)
- return result.guid;
-
- // Give the uri a GUID if it doesn't have one
- return this._setGUID(id);
- },
-
- get _idForGUIDStm() {
- return this._getStmt(
- "SELECT id AS item_id " +
- "FROM moz_bookmarks " +
- "WHERE guid = :guid");
- },
- _idForGUIDCols: ["item_id"],
-
- // noCreate is provided as an optional argument to prevent the creation of
- // non-existent special records, such as "mobile".
- idForGUID: function idForGUID(guid, noCreate) {
- if (kSpecialIds.isSpecialGUID(guid))
- return kSpecialIds.specialIdForGUID(guid, !noCreate);
-
- let stmt = this._idForGUIDStm;
- // guid might be a String object rather than a string.
- stmt.params.guid = guid.toString();
-
- let results = Async.querySpinningly(stmt, this._idForGUIDCols);
- this._log.trace("Number of rows matching GUID " + guid + ": "
- + results.length);
-
- // Here's the one we care about: the first.
- let result = results[0];
-
- if (!result)
- return -1;
-
- return result.item_id;
- },
-
- _calculateIndex: function _calculateIndex(record) {
- // Ensure folders have a very high sort index so they're not synced last.
- if (record.type == "folder")
- return FOLDER_SORTINDEX;
-
- // For anything directly under the toolbar, give it a boost of more than an
- // unvisited bookmark
- let index = 0;
- if (record.parentid == "toolbar")
- index += 150;
-
- // Add in the bookmark's frecency if we have something.
- if (record.bmkUri != null) {
- this._frecencyStm.params.url = record.bmkUri;
- let result = Async.querySpinningly(this._frecencyStm, this._frecencyCols);
- if (result.length)
- index += result[0].frecency;
- }
-
- return index;
- },
-
- _getChildren: function BStore_getChildren(guid, items) {
- let node = guid; // the recursion case
- if (typeof(node) == "string") { // callers will give us the guid as the first arg
- let nodeID = this.idForGUID(guid, true);
- if (!nodeID) {
- this._log.debug("No node for GUID " + guid + "; returning no children.");
- return items;
- }
- node = this._getNode(nodeID);
- }
-
- if (node.type == node.RESULT_TYPE_FOLDER) {
- node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
- node.containerOpen = true;
- try {
- // Remember all the children GUIDs and recursively get more
- for (let i = 0; i < node.childCount; i++) {
- let child = node.getChild(i);
- items[this.GUIDForId(child.itemId)] = true;
- this._getChildren(child, items);
- }
- }
- finally {
- node.containerOpen = false;
- }
- }
-
- return items;
- },
-
- /**
- * Associates the URI of the item with the provided ID with the
- * provided array of tags.
- * If the provided ID does not identify an item with a URI,
- * returns immediately.
- */
- _tagID: function _tagID(itemID, tags) {
- if (!itemID || !tags) {
- return;
- }
-
- try {
- let u = PlacesUtils.bookmarks.getBookmarkURI(itemID);
- this._tagURI(u, tags);
- } catch (e) {
- this._log.warn("Got exception fetching URI for " + itemID + ": not tagging. ", e);
-
- // I guess it doesn't have a URI. Don't try to tag it.
- return;
- }
- },
-
- /**
- * Associate the provided URI with the provided array of tags.
- * If the provided URI is falsy, returns immediately.
- */
- _tagURI: function _tagURI(bookmarkURI, tags) {
- if (!bookmarkURI || !tags) {
- return;
- }
-
- // Filter out any null/undefined/empty tags.
- tags = tags.filter(t => t);
-
- // Temporarily tag a dummy URI to preserve tag ids when untagging.
- let dummyURI = Utils.makeURI("about:weave#BStore_tagURI");
- PlacesUtils.tagging.tagURI(dummyURI, tags);
- PlacesUtils.tagging.untagURI(bookmarkURI, null);
- PlacesUtils.tagging.tagURI(bookmarkURI, tags);
- PlacesUtils.tagging.untagURI(dummyURI, null);
- },
-
- getAllIDs: function BStore_getAllIDs() {
- let items = {"menu": true,
- "toolbar": true};
- for each (let guid in kSpecialIds.guids) {
- if (guid != "places" && guid != "tags")
- this._getChildren(guid, items);
- }
- return items;
- },
-
- wipe: function BStore_wipe() {
- let cb = Async.makeSpinningCallback();
- Task.spawn(function() {
- // Save a backup before clearing out all bookmarks.
- yield PlacesBackups.create(null, true);
- for each (let guid in kSpecialIds.guids)
- if (guid != "places") {
- let id = kSpecialIds.specialIdForGUID(guid);
- if (id)
- PlacesUtils.bookmarks.removeFolderChildren(id);
- }
- cb();
- });
- cb.wait();
- }
-};
-
-function BookmarksTracker(name, engine) {
- Tracker.call(this, name, engine);
-
- Svc.Obs.add("places-shutdown", this);
-}
-BookmarksTracker.prototype = {
- __proto__: Tracker.prototype,
-
- startTracking: function() {
- PlacesUtils.bookmarks.addObserver(this, true);
- Svc.Obs.add("bookmarks-restore-begin", this);
- Svc.Obs.add("bookmarks-restore-success", this);
- Svc.Obs.add("bookmarks-restore-failed", this);
- },
-
- stopTracking: function() {
- PlacesUtils.bookmarks.removeObserver(this);
- Svc.Obs.remove("bookmarks-restore-begin", this);
- Svc.Obs.remove("bookmarks-restore-success", this);
- Svc.Obs.remove("bookmarks-restore-failed", this);
- },
-
- observe: function observe(subject, topic, data) {
- Tracker.prototype.observe.call(this, subject, topic, data);
-
- switch (topic) {
- case "bookmarks-restore-begin":
- this._log.debug("Ignoring changes from importing bookmarks.");
- this.ignoreAll = true;
- break;
- case "bookmarks-restore-success":
- this._log.debug("Tracking all items on successful import.");
- this.ignoreAll = false;
-
- this._log.debug("Restore succeeded: wiping server and other clients.");
- this.engine.service.resetClient([this.name]);
- this.engine.service.wipeServer([this.name]);
- this.engine.service.clientsEngine.sendCommand("wipeEngine", [this.name]);
- break;
- case "bookmarks-restore-failed":
- this._log.debug("Tracking all items on failed import.");
- this.ignoreAll = false;
- break;
- }
- },
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsINavBookmarkObserver,
- Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS,
- Ci.nsISupportsWeakReference
- ]),
-
- /**
- * Add a bookmark GUID to be uploaded and bump up the sync score.
- *
- * @param itemGuid
- * GUID of the bookmark to upload.
- */
- _add: function BMT__add(itemId, guid) {
- guid = kSpecialIds.specialGUIDForId(itemId) || guid;
- if (this.addChangedID(guid))
- this._upScore();
- },
-
- /* Every add/remove/change will trigger a sync for MULTI_DEVICE. */
- _upScore: function BMT__upScore() {
- this.score += SCORE_INCREMENT_XLARGE;
- },
-
- /**
- * Determine if a change should be ignored.
- *
- * @param itemId
- * Item under consideration to ignore
- * @param folder (optional)
- * Folder of the item being changed
- */
- _ignore: function BMT__ignore(itemId, folder, guid) {
- // Ignore unconditionally if the engine tells us to.
- if (this.ignoreAll)
- return true;
-
- // Get the folder id if we weren't given one.
- if (folder == null) {
- try {
- folder = PlacesUtils.bookmarks.getFolderIdForItem(itemId);
- } catch (ex) {
- this._log.debug("getFolderIdForItem(" + itemId +
- ") threw; calling _ensureMobileQuery.");
- // I'm guessing that gFIFI can throw, and perhaps that's why
- // _ensureMobileQuery is here at all. Try not to call it.
- this._ensureMobileQuery();
- folder = PlacesUtils.bookmarks.getFolderIdForItem(itemId);
- }
- }
-
- // Ignore changes to tags (folders under the tags folder).
- let tags = kSpecialIds.tags;
- if (folder == tags)
- return true;
-
- // Ignore tag items (the actual instance of a tag for a bookmark).
- if (PlacesUtils.bookmarks.getFolderIdForItem(folder) == tags)
- return true;
-
- // Make sure to remove items that have the exclude annotation.
- if (PlacesUtils.annotations.itemHasAnnotation(itemId, EXCLUDEBACKUP_ANNO)) {
- this.removeChangedID(guid);
- return true;
- }
-
- return false;
- },
-
- onItemAdded: function BMT_onItemAdded(itemId, folder, index,
- itemType, uri, title, dateAdded,
- guid, parentGuid) {
- if (this._ignore(itemId, folder, guid))
- return;
-
- this._log.trace("onItemAdded: " + itemId);
- this._add(itemId, guid);
- this._add(folder, parentGuid);
- },
-
- onItemRemoved: function (itemId, parentId, index, type, uri,
- guid, parentGuid) {
- if (this._ignore(itemId, parentId, guid)) {
- return;
- }
-
- this._log.trace("onItemRemoved: " + itemId);
- this._add(itemId, guid);
- this._add(parentId, parentGuid);
- },
-
- _ensureMobileQuery: function _ensureMobileQuery() {
- let find = val =>
- PlacesUtils.annotations.getItemsWithAnnotation(ORGANIZERQUERY_ANNO, {}).filter(
- id => PlacesUtils.annotations.getItemAnnotation(id, ORGANIZERQUERY_ANNO) == val
- );
-
- // Don't continue if the Library isn't ready
- let all = find(ALLBOOKMARKS_ANNO);
- if (all.length == 0)
- return;
-
- // Disable handling of notifications while changing the mobile query
- this.ignoreAll = true;
-
- let mobile = find(MOBILE_ANNO);
- let queryURI = Utils.makeURI("place:folder=" + kSpecialIds.mobile);
- let title = Str.sync.get("mobile.label");
-
- // Don't add OR remove the mobile bookmarks if there's nothing.
- if (PlacesUtils.bookmarks.getIdForItemAt(kSpecialIds.mobile, 0) == -1) {
- if (mobile.length != 0)
- PlacesUtils.bookmarks.removeItem(mobile[0]);
- }
- // Add the mobile bookmarks query if it doesn't exist
- else if (mobile.length == 0) {
- let query = PlacesUtils.bookmarks.insertBookmark(all[0], queryURI, -1, title);
- PlacesUtils.annotations.setItemAnnotation(query, ORGANIZERQUERY_ANNO, MOBILE_ANNO, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- PlacesUtils.annotations.setItemAnnotation(query, EXCLUDEBACKUP_ANNO, 1, 0,
- PlacesUtils.annotations.EXPIRE_NEVER);
- }
- // Make sure the existing title is correct
- else if (PlacesUtils.bookmarks.getItemTitle(mobile[0]) != title) {
- PlacesUtils.bookmarks.setItemTitle(mobile[0], title);
- }
-
- this.ignoreAll = false;
- },
-
- // This method is oddly structured, but the idea is to return as quickly as
- // possible -- this handler gets called *every time* a bookmark changes, for
- // *each change*.
- onItemChanged: function BMT_onItemChanged(itemId, property, isAnno, value,
- lastModified, itemType, parentId,
- guid, parentGuid) {
- // Quicker checks first.
- if (this.ignoreAll)
- return;
-
- if (isAnno && (ANNOS_TO_TRACK.indexOf(property) == -1))
- // Ignore annotations except for the ones that we sync.
- return;
-
- // Ignore favicon changes to avoid unnecessary churn.
- if (property == "favicon")
- return;
-
- if (this._ignore(itemId, parentId, guid))
- return;
-
- this._log.trace("onItemChanged: " + itemId +
- (", " + property + (isAnno? " (anno)" : "")) +
- (value ? (" = \"" + value + "\"") : ""));
- this._add(itemId, guid);
- },
-
- onItemMoved: function BMT_onItemMoved(itemId, oldParent, oldIndex,
- newParent, newIndex, itemType,
- guid, oldParentGuid, newParentGuid) {
- if (this._ignore(itemId, newParent, guid))
- return;
-
- this._log.trace("onItemMoved: " + itemId);
- this._add(oldParent, oldParentGuid);
- if (oldParent != newParent) {
- this._add(itemId, guid);
- this._add(newParent, newParentGuid);
- }
-
- // Remove any position annotations now that the user moved the item
- PlacesUtils.annotations.removeItemAnnotation(itemId, PARENT_ANNO);
- },
-
- onBeginUpdateBatch: function () {},
- onEndUpdateBatch: function () {},
- onItemVisited: function () {}
-};