--- a/toolkit/components/places/PlacesSyncUtils.jsm
+++ b/toolkit/components/places/PlacesSyncUtils.jsm
@@ -447,22 +447,18 @@ var annotateOrphan = Task.async(function
PlacesUtils.annotations.EXPIRE_NEVER,
SOURCE_SYNC);
});
var reparentOrphans = Task.async(function* (item) {
if (item.kind != BookmarkSyncUtils.KINDS.FOLDER) {
return;
}
- let orphanIds = findAnnoItems(BookmarkSyncUtils.SYNC_PARENT_ANNO, item.syncId);
- // The annotations API returns item IDs, but the asynchronous bookmarks
- // API uses GUIDs. We can remove the `promiseItemGuid` calls and parallel
- // arrays once we implement a GUID-aware annotations API.
- let orphanGuids = yield Promise.all(orphanIds.map(id =>
- PlacesUtils.promiseItemGuid(id)));
+ let orphanGuids = yield fetchGuidsWithAnno(BookmarkSyncUtils.SYNC_PARENT_ANNO,
+ item.syncId);
let folderGuid = BookmarkSyncUtils.syncIdToGuid(item.syncId);
BookmarkSyncLog.debug(`reparentOrphans: Reparenting ${
JSON.stringify(orphanGuids)} to ${item.syncId}`);
for (let i = 0; i < orphanGuids.length; ++i) {
let isReparented = false;
try {
// Reparenting can fail if we have a corrupted or incomplete tree
// where an item's parent is one of its descendants.
@@ -476,17 +472,18 @@ var reparentOrphans = Task.async(functio
});
isReparented = true;
} catch (ex) {
BookmarkSyncLog.error(`reparentOrphans: Failed to reparent item ${
orphanGuids[i]} to ${item.syncId}`, ex);
}
if (isReparented) {
// Remove the annotation once we've reparented the item.
- PlacesUtils.annotations.removeItemAnnotation(orphanIds[i],
+ let orphanId = yield PlacesUtils.promiseItemId(orphanGuids[i]);
+ PlacesUtils.annotations.removeItemAnnotation(orphanId,
BookmarkSyncUtils.SYNC_PARENT_ANNO, SOURCE_SYNC);
}
}
});
// Inserts a synced bookmark into the database.
var insertSyncBookmark = Task.async(function* (insertInfo) {
let requestedParentSyncId = insertInfo.parentSyncId;
@@ -536,19 +533,18 @@ var insertSyncBookmark = Task.async(func
// Inserts a synced livemark.
var insertSyncLivemark = Task.async(function* (insertInfo) {
if (!insertInfo.feed) {
BookmarkSyncLog.debug(`insertSyncLivemark: ${
insertInfo.syncId} missing feed URL`);
return null;
}
let livemarkInfo = syncBookmarkToPlacesBookmark(insertInfo);
- let parentId = yield PlacesUtils.promiseItemId(livemarkInfo.parentGuid);
- let parentIsLivemark = PlacesUtils.annotations.itemHasAnnotation(parentId,
- PlacesUtils.LMANNO_FEEDURI);
+ let parentIsLivemark = yield getAnno(livemarkInfo.parentGuid,
+ PlacesUtils.LMANNO_FEEDURI);
if (parentIsLivemark) {
// A livemark can't be a descendant of another livemark.
BookmarkSyncLog.debug(`insertSyncLivemark: Invalid parent ${
insertInfo.parentSyncId}; skipping livemark record ${
insertInfo.syncId}`);
return null;
}
@@ -605,19 +601,18 @@ var insertBookmarkMetadata = Task.async(
return newItem;
});
// Determines the Sync record kind for an existing bookmark.
var getKindForItem = Task.async(function* (item) {
switch (item.type) {
case PlacesUtils.bookmarks.TYPE_FOLDER: {
- let itemId = yield PlacesUtils.promiseItemId(item.guid);
- let isLivemark = PlacesUtils.annotations.itemHasAnnotation(itemId,
- PlacesUtils.LMANNO_FEEDURI);
+ let isLivemark = yield getAnno(item.guid,
+ PlacesUtils.LMANNO_FEEDURI);
return isLivemark ? BookmarkSyncUtils.KINDS.LIVEMARK :
BookmarkSyncUtils.KINDS.FOLDER;
}
case PlacesUtils.bookmarks.TYPE_BOOKMARK:
return item.url.protocol == "place:" ?
BookmarkSyncUtils.KINDS.QUERY :
BookmarkSyncUtils.KINDS.BOOKMARK;
@@ -844,23 +839,16 @@ var updateBookmarkMetadata = Task.async(
PlacesUtils.annotations.EXPIRE_NEVER,
SOURCE_SYNC);
newItem.query = updateInfo.query;
}
return newItem;
});
-var setGuid = Task.async(function* (db, itemId, newGuid) {
- yield db.executeCached(`UPDATE moz_bookmarks SET guid = :newGuid
- WHERE id = :itemId`, { newGuid, itemId });
- PlacesUtils.invalidateCachedGuidFor(itemId);
- return newGuid;
-});
-
function validateNewBookmark(info) {
let insertInfo = validateSyncBookmarkObject(info,
{ kind: { required: true }
, syncId: { required: true }
, url: { requiredIf: b => [ BookmarkSyncUtils.KINDS.BOOKMARK
, BookmarkSyncUtils.KINDS.MICROSUMMARY
, BookmarkSyncUtils.KINDS.QUERY ].includes(b.kind)
, validIf: b => [ BookmarkSyncUtils.KINDS.BOOKMARK
@@ -890,21 +878,41 @@ function validateNewBookmark(info) {
, BookmarkSyncUtils.KINDS.QUERY ].includes(b.kind) }
, feed: { validIf: b => b.kind == BookmarkSyncUtils.KINDS.LIVEMARK }
, site: { validIf: b => b.kind == BookmarkSyncUtils.KINDS.LIVEMARK }
});
return insertInfo;
}
-function findAnnoItems(anno, val) {
- let annos = PlacesUtils.annotations;
- return annos.getItemsWithAnnotation(anno, {}).filter(id =>
- annos.getItemAnnotation(id, anno) == val);
-}
+// Returns an array of GUIDs for items that have an `anno` with the given `val`.
+var fetchGuidsWithAnno = Task.async(function* (anno, val) {
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.executeCached(`
+ SELECT b.guid FROM moz_items_annos a
+ JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
+ JOIN moz_bookmarks b ON b.id = a.item_id
+ WHERE n.name = :anno AND
+ a.content = :val`,
+ { anno, val });
+ return rows.map(row => row.getResultByName("guid"));
+});
+
+// Returns the value of an item's annotation, or `null` if it's not set.
+var getAnno = Task.async(function* (guid, anno) {
+ let db = yield PlacesUtils.promiseDBConnection();
+ let rows = yield db.executeCached(`
+ SELECT a.content FROM moz_items_annos a
+ JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
+ JOIN moz_bookmarks b ON b.id = a.item_id
+ WHERE b.guid = :guid AND
+ n.name = :anno`,
+ { guid, anno });
+ return rows.length ? rows[0].getResultByName("content") : null;
+});
var tagItem = Task.async(function (item, tags) {
if (!item.url) {
return [];
}
// Remove leading and trailing whitespace, then filter out empty tags.
let newTags = tags.map(tag => tag.trim()).filter(Boolean);
@@ -1048,94 +1056,89 @@ function syncBookmarkToPlacesBookmark(in
}
return bookmarkInfo;
}
// Creates and returns a Sync bookmark object containing the bookmark's
// tags, keyword, description, and whether it loads in the sidebar.
var fetchBookmarkItem = Task.async(function* (bookmarkItem) {
- let itemId = yield PlacesUtils.promiseItemId(bookmarkItem.guid);
let item = yield placesBookmarkToSyncBookmark(bookmarkItem);
item.tags = PlacesUtils.tagging.getTagsForURI(
PlacesUtils.toURI(bookmarkItem.url), {});
let keywordEntry = yield PlacesUtils.keywords.fetch({
url: bookmarkItem.url,
});
if (keywordEntry) {
item.keyword = keywordEntry.keyword;
}
- let description = getItemDescription(itemId);
+ let description = yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.DESCRIPTION_ANNO);
if (description) {
item.description = description;
}
- item.loadInSidebar = PlacesUtils.annotations.itemHasAnnotation(itemId,
- BookmarkSyncUtils.SIDEBAR_ANNO);
+ item.loadInSidebar = !!(yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.SIDEBAR_ANNO));
return item;
});
// Creates and returns a Sync bookmark object containing the folder's
// description and children.
var fetchFolderItem = Task.async(function* (bookmarkItem) {
- let itemId = yield PlacesUtils.promiseItemId(bookmarkItem.guid);
let item = yield placesBookmarkToSyncBookmark(bookmarkItem);
- let description = getItemDescription(itemId);
+ let description = yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.DESCRIPTION_ANNO);
if (description) {
item.description = description;
}
let db = yield PlacesUtils.promiseDBConnection();
let children = yield fetchAllChildren(db, bookmarkItem.guid);
item.childSyncIds = children.map(child =>
BookmarkSyncUtils.guidToSyncId(child.guid)
);
return item;
});
// Creates and returns a Sync bookmark object containing the livemark's
// description, children (none), feed URI, and site URI.
var fetchLivemarkItem = Task.async(function* (bookmarkItem) {
- let itemId = yield PlacesUtils.promiseItemId(bookmarkItem.guid);
let item = yield placesBookmarkToSyncBookmark(bookmarkItem);
- let description = getItemDescription(itemId);
+ let description = yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.DESCRIPTION_ANNO);
if (description) {
item.description = description;
}
- let feedAnno = PlacesUtils.annotations.getItemAnnotation(itemId,
- PlacesUtils.LMANNO_FEEDURI);
+ let feedAnno = yield getAnno(bookmarkItem.guid, PlacesUtils.LMANNO_FEEDURI);
item.feed = new URL(feedAnno);
- let siteAnno = null;
- try {
- siteAnno = PlacesUtils.annotations.getItemAnnotation(itemId,
- PlacesUtils.LMANNO_SITEURI);
- } catch (ex) {}
- if (siteAnno != null) {
+ let siteAnno = yield getAnno(bookmarkItem.guid, PlacesUtils.LMANNO_SITEURI);
+ if (siteAnno) {
item.site = new URL(siteAnno);
}
return item;
});
// Creates and returns a Sync bookmark object containing the query's tag
// folder name and smart bookmark query ID.
var fetchQueryItem = Task.async(function* (bookmarkItem) {
- let itemId = yield PlacesUtils.promiseItemId(bookmarkItem.guid);
let item = yield placesBookmarkToSyncBookmark(bookmarkItem);
- let description = getItemDescription(itemId);
+ let description = yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.DESCRIPTION_ANNO);
if (description) {
item.description = description;
}
let folder = null;
let params = new URLSearchParams(bookmarkItem.url.pathname);
let tagFolderId = +params.get("folder");
if (tagFolderId) {
@@ -1147,31 +1150,16 @@ var fetchQueryItem = Task.async(function
BookmarkSyncLog.warn("fetchQueryItem: Query " + bookmarkItem.url.href +
" points to nonexistent folder " + tagFolderId, ex);
}
}
if (folder != null) {
item.folder = folder;
}
- let query = null;
- try {
- // Throws if the bookmark doesn't have the smart bookmark anno.
- query = PlacesUtils.annotations.getItemAnnotation(itemId,
- BookmarkSyncUtils.SMART_BOOKMARKS_ANNO);
- } catch (ex) {}
- if (query != null) {
+ let query = yield getAnno(bookmarkItem.guid,
+ BookmarkSyncUtils.SMART_BOOKMARKS_ANNO);
+ if (query) {
item.query = query;
}
return item;
});
-
-// Returns an item's description, or `null` if one isn't set.
-function getItemDescription(id) {
- try {
- return PlacesUtils.annotations.getItemAnnotation(id,
- BookmarkSyncUtils.DESCRIPTION_ANNO);
- } catch (ex) {}
- return null;
-}
-
-