mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Bug 1258127 - Update the bookmarks engine to pull changes from Places. r=markh
MozReview-Commit-ID: 4YESuxP2rRf --HG-- extra : rebase_source : 9c2f5830d10ba280e45c30076f19f498e6913fd0
This commit is contained in:
parent
308045ed35
commit
ccf45973d0
@ -1303,8 +1303,11 @@ SyncEngine.prototype = {
|
||||
|
||||
_deleteId: function (id) {
|
||||
this._tracker.removeChangedID(id);
|
||||
this._noteDeletedId(id);
|
||||
},
|
||||
|
||||
// Remember this id to delete at the end of sync
|
||||
// Marks an ID for deletion at the end of the sync.
|
||||
_noteDeletedId(id) {
|
||||
if (this._delete.ids == null)
|
||||
this._delete.ids = [id];
|
||||
else
|
||||
@ -1631,9 +1634,12 @@ SyncEngine.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark failed WBOs as changed again so they are reuploaded next time.
|
||||
this.trackRemainingChanges();
|
||||
this._modified.clear();
|
||||
try {
|
||||
// Mark failed WBOs as changed again so they are reuploaded next time.
|
||||
this.trackRemainingChanges();
|
||||
} finally {
|
||||
this._modified.clear();
|
||||
}
|
||||
},
|
||||
|
||||
_sync: function () {
|
||||
|
@ -40,8 +40,6 @@ const {
|
||||
SOURCE_IMPORT_REPLACE,
|
||||
} = Ci.nsINavBookmarksService;
|
||||
|
||||
const SQLITE_MAX_VARIABLE_NUMBER = 999;
|
||||
|
||||
const ORGANIZERQUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
|
||||
const ALLBOOKMARKS_ANNO = "AllBookmarks";
|
||||
const MOBILE_ANNO = "MobileBookmarks";
|
||||
@ -63,6 +61,13 @@ const FORBIDDEN_INCOMING_PARENT_IDS = ["pinned", "readinglist"];
|
||||
// the tracker doesn't currently distinguish between the two.
|
||||
const IGNORED_SOURCES = [SOURCE_SYNC, SOURCE_IMPORT, SOURCE_IMPORT_REPLACE];
|
||||
|
||||
function isSyncedRootNode(node) {
|
||||
return node.root == "bookmarksMenuFolder" ||
|
||||
node.root == "unfiledBookmarksFolder" ||
|
||||
node.root == "toolbarFolder" ||
|
||||
node.root == "mobileFolder";
|
||||
}
|
||||
|
||||
// Returns the constructor for a bookmark record type.
|
||||
function getTypeObject(type) {
|
||||
switch (type) {
|
||||
@ -302,9 +307,8 @@ BookmarksEngine.prototype = {
|
||||
_buildGUIDMap: function _buildGUIDMap() {
|
||||
let store = this._store;
|
||||
let guidMap = {};
|
||||
let tree = Async.promiseSpinningly(PlacesUtils.promiseBookmarksTree("", {
|
||||
includeItemIds: true
|
||||
}));
|
||||
let tree = Async.promiseSpinningly(PlacesUtils.promiseBookmarksTree(""));
|
||||
|
||||
function* walkBookmarksTree(tree, parent=null) {
|
||||
if (tree) {
|
||||
// Skip root node
|
||||
@ -320,19 +324,15 @@ BookmarksEngine.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
function* walkBookmarksRoots(tree, rootIDs) {
|
||||
for (let id of rootIDs) {
|
||||
let bookmarkRoot = tree.children.find(child => child.id === id);
|
||||
if (bookmarkRoot === null) {
|
||||
continue;
|
||||
function* walkBookmarksRoots(tree) {
|
||||
for (let child of tree.children) {
|
||||
if (isSyncedRootNode(child)) {
|
||||
yield* walkBookmarksTree(child, tree);
|
||||
}
|
||||
yield* walkBookmarksTree(bookmarkRoot, tree);
|
||||
}
|
||||
}
|
||||
|
||||
let rootsToWalk = getChangeRootIds();
|
||||
|
||||
for (let [node, parent] of walkBookmarksRoots(tree, rootsToWalk)) {
|
||||
for (let [node, parent] of walkBookmarksRoots(tree)) {
|
||||
let {guid, id, type: placeType} = node;
|
||||
guid = PlacesSyncUtils.bookmarks.guidToSyncId(guid);
|
||||
let key;
|
||||
@ -570,6 +570,11 @@ BookmarksEngine.prototype = {
|
||||
if (entry != null && entry.hasDupe) {
|
||||
record.hasDupe = true;
|
||||
}
|
||||
if (record.deleted) {
|
||||
// Make sure deleted items are marked as tombstones. This handles the
|
||||
// case where a changed item is deleted during a sync.
|
||||
this._modified.setTombstone(record.id);
|
||||
}
|
||||
return record;
|
||||
},
|
||||
|
||||
@ -590,83 +595,26 @@ BookmarksEngine.prototype = {
|
||||
},
|
||||
|
||||
pullAllChanges() {
|
||||
return new BookmarksChangeset(this._store.getAllIDs());
|
||||
return this.pullNewChanges();
|
||||
},
|
||||
|
||||
pullNewChanges() {
|
||||
let modifiedGUIDs = this._getModifiedGUIDs();
|
||||
if (!modifiedGUIDs.length) {
|
||||
return new BookmarksChangeset(this._tracker.changedIDs);
|
||||
}
|
||||
|
||||
// We don't use `PlacesUtils.promiseDBConnection` here because
|
||||
// `getChangedIDs` might be called while we're in a batch, meaning we
|
||||
// won't see any changes until the batch finishes and the transaction
|
||||
// commits.
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.DBConnection;
|
||||
|
||||
// Filter out tags, organizer queries, and other descendants that we're
|
||||
// not tracking. We chunk `modifiedGUIDs` because SQLite limits the number
|
||||
// of bound parameters per query.
|
||||
for (let startIndex = 0;
|
||||
startIndex < modifiedGUIDs.length;
|
||||
startIndex += SQLITE_MAX_VARIABLE_NUMBER) {
|
||||
|
||||
let chunkLength = Math.min(SQLITE_MAX_VARIABLE_NUMBER,
|
||||
modifiedGUIDs.length - startIndex);
|
||||
|
||||
let query = `
|
||||
WITH RECURSIVE
|
||||
modifiedGuids(guid) AS (
|
||||
VALUES ${new Array(chunkLength).fill("(?)").join(", ")}
|
||||
),
|
||||
syncedItems(id) AS (
|
||||
VALUES ${getChangeRootIds().map(id => `(${id})`).join(", ")}
|
||||
UNION ALL
|
||||
SELECT b.id
|
||||
FROM moz_bookmarks b
|
||||
JOIN syncedItems s ON b.parent = s.id
|
||||
)
|
||||
SELECT b.guid
|
||||
FROM modifiedGuids m
|
||||
JOIN moz_bookmarks b ON b.guid = m.guid
|
||||
LEFT JOIN syncedItems s ON b.id = s.id
|
||||
WHERE s.id IS NULL
|
||||
`;
|
||||
|
||||
let statement = db.createAsyncStatement(query);
|
||||
try {
|
||||
for (let i = 0; i < chunkLength; i++) {
|
||||
statement.bindByIndex(i, modifiedGUIDs[startIndex + i]);
|
||||
}
|
||||
let results = Async.querySpinningly(statement, ["guid"]);
|
||||
for (let { guid } of results) {
|
||||
let syncID = PlacesSyncUtils.bookmarks.guidToSyncId(guid);
|
||||
this._tracker.removeChangedID(syncID);
|
||||
}
|
||||
} finally {
|
||||
statement.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
return new BookmarksChangeset(this._tracker.changedIDs);
|
||||
let changes = Async.promiseSpinningly(this._tracker.promiseChangedIDs());
|
||||
return new BookmarksChangeset(changes);
|
||||
},
|
||||
|
||||
// Returns an array of Places GUIDs for all changed items. Ignores deletions,
|
||||
// which won't exist in the DB and shouldn't be removed from the tracker.
|
||||
_getModifiedGUIDs() {
|
||||
let guids = [];
|
||||
for (let syncID in this._tracker.changedIDs) {
|
||||
if (this._tracker.changedIDs[syncID].deleted === true) {
|
||||
// The `===` check also filters out old persisted timestamps,
|
||||
// which won't have a `deleted` property.
|
||||
continue;
|
||||
}
|
||||
let guid = PlacesSyncUtils.bookmarks.syncIdToGuid(syncID);
|
||||
guids.push(guid);
|
||||
}
|
||||
return guids;
|
||||
trackRemainingChanges() {
|
||||
let changes = this._modified.changes;
|
||||
Async.promiseSpinningly(PlacesSyncUtils.bookmarks.pushChanges(changes));
|
||||
},
|
||||
|
||||
_deleteId(id) {
|
||||
this._noteDeletedId(id);
|
||||
},
|
||||
|
||||
resetClient() {
|
||||
SyncEngine.prototype.resetClient.call(this);
|
||||
Async.promiseSpinningly(PlacesSyncUtils.bookmarks.reset());
|
||||
},
|
||||
|
||||
// Called when _findDupe returns a dupe item and the engine has decided to
|
||||
@ -1054,50 +1002,28 @@ BookmarksStore.prototype = {
|
||||
return index;
|
||||
},
|
||||
|
||||
getAllIDs: function BStore_getAllIDs() {
|
||||
let items = {};
|
||||
|
||||
let query = `
|
||||
WITH RECURSIVE
|
||||
changeRootContents(id) AS (
|
||||
VALUES ${getChangeRootIds().map(id => `(${id})`).join(", ")}
|
||||
UNION ALL
|
||||
SELECT b.id
|
||||
FROM moz_bookmarks b
|
||||
JOIN changeRootContents c ON b.parent = c.id
|
||||
)
|
||||
SELECT guid
|
||||
FROM changeRootContents
|
||||
JOIN moz_bookmarks USING (id)
|
||||
`;
|
||||
|
||||
let statement = this._getStmt(query);
|
||||
let results = Async.querySpinningly(statement, ["guid"]);
|
||||
for (let { guid } of results) {
|
||||
let syncID = PlacesSyncUtils.bookmarks.guidToSyncId(guid);
|
||||
items[syncID] = { modified: 0, deleted: false };
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
wipe: function BStore_wipe() {
|
||||
this.clearPendingDeletions();
|
||||
Async.promiseSpinningly(Task.spawn(function* () {
|
||||
// Save a backup before clearing out all bookmarks.
|
||||
yield PlacesBackups.create(null, true);
|
||||
yield PlacesUtils.bookmarks.eraseEverything({
|
||||
source: SOURCE_SYNC,
|
||||
});
|
||||
yield PlacesSyncUtils.bookmarks.wipe();
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// The bookmarks tracker is a special flower. Instead of listening for changes
|
||||
// via observer notifications, it queries Places for the set of items that have
|
||||
// changed since the last sync. Because it's a "pull-based" tracker, it ignores
|
||||
// all concepts of "add a changed ID." However, it still registers an observer
|
||||
// to bump the score, so that changed bookmarks are synced immediately.
|
||||
function BookmarksTracker(name, engine) {
|
||||
this._batchDepth = 0;
|
||||
this._batchSawScoreIncrement = false;
|
||||
Tracker.call(this, name, engine);
|
||||
|
||||
delete this.changedIDs; // so our getter/setter takes effect.
|
||||
|
||||
Svc.Obs.add("places-shutdown", this);
|
||||
}
|
||||
BookmarksTracker.prototype = {
|
||||
@ -1113,6 +1039,10 @@ BookmarksTracker.prototype = {
|
||||
// setting a read-only property.
|
||||
set ignoreAll(value) {},
|
||||
|
||||
// We never want to persist changed IDs, as the changes are already stored
|
||||
// in Places.
|
||||
persistChangedIDs: false,
|
||||
|
||||
startTracking: function() {
|
||||
PlacesUtils.bookmarks.addObserver(this, true);
|
||||
Svc.Obs.add("bookmarks-restore-begin", this);
|
||||
@ -1127,6 +1057,46 @@ BookmarksTracker.prototype = {
|
||||
Svc.Obs.remove("bookmarks-restore-failed", this);
|
||||
},
|
||||
|
||||
// Ensure we aren't accidentally using the base persistence.
|
||||
addChangedID(id, when) {
|
||||
throw new Error("Don't add IDs to the bookmarks tracker");
|
||||
},
|
||||
|
||||
removeChangedID(id) {
|
||||
throw new Error("Don't remove IDs from the bookmarks tracker");
|
||||
},
|
||||
|
||||
// This method is called at various times, so we override with a no-op
|
||||
// instead of throwing.
|
||||
clearChangedIDs() {},
|
||||
|
||||
saveChangedIDs(cb) {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
},
|
||||
|
||||
loadChangedIDs(cb) {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
},
|
||||
|
||||
promiseChangedIDs() {
|
||||
return PlacesSyncUtils.bookmarks.pullChanges();
|
||||
},
|
||||
|
||||
get changedIDs() {
|
||||
throw new Error("Use promiseChangedIDs");
|
||||
},
|
||||
|
||||
set changedIDs(obj) {
|
||||
// let engine init set it to nothing.
|
||||
if (Object.keys(obj).length != 0) {
|
||||
throw new Error("Don't set initial changed bookmark IDs");
|
||||
}
|
||||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
Tracker.prototype.observe.call(this, subject, topic, data);
|
||||
|
||||
@ -1154,52 +1124,6 @@ BookmarksTracker.prototype = {
|
||||
Ci.nsISupportsWeakReference
|
||||
]),
|
||||
|
||||
addChangedID(id, change) {
|
||||
if (!id) {
|
||||
this._log.warn("Attempted to add undefined ID to tracker");
|
||||
return false;
|
||||
}
|
||||
if (this._ignored.includes(id)) {
|
||||
return false;
|
||||
}
|
||||
let shouldSaveChange = false;
|
||||
let currentChange = this.changedIDs[id];
|
||||
if (currentChange) {
|
||||
if (typeof currentChange == "number") {
|
||||
// Allow raw timestamps for backward-compatibility with persisted
|
||||
// changed IDs. The new format uses tuples to track deleted items.
|
||||
shouldSaveChange = currentChange < change.modified;
|
||||
} else {
|
||||
shouldSaveChange = currentChange.modified < change.modified ||
|
||||
currentChange.deleted != change.deleted;
|
||||
}
|
||||
} else {
|
||||
shouldSaveChange = true;
|
||||
}
|
||||
if (shouldSaveChange) {
|
||||
this._saveChangedID(id, change);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a bookmark GUID to be uploaded and bump up the sync score.
|
||||
*
|
||||
* @param itemId
|
||||
* The Places item ID of the bookmark to upload.
|
||||
* @param guid
|
||||
* The Places GUID of the bookmark to upload.
|
||||
* @param isTombstone
|
||||
* Whether we're uploading a tombstone for a removed bookmark.
|
||||
*/
|
||||
_add: function BMT__add(itemId, guid, isTombstone = false) {
|
||||
let syncID = PlacesSyncUtils.bookmarks.guidToSyncId(guid);
|
||||
let info = { modified: Date.now() / 1000, deleted: isTombstone };
|
||||
if (this.addChangedID(syncID, info)) {
|
||||
this._upScore();
|
||||
}
|
||||
},
|
||||
|
||||
/* Every add/remove/change will trigger a sync for MULTI_DEVICE (except in
|
||||
a batch operation, where we do it at the end of the batch) */
|
||||
_upScore: function BMT__upScore() {
|
||||
@ -1218,8 +1142,7 @@ BookmarksTracker.prototype = {
|
||||
}
|
||||
|
||||
this._log.trace("onItemAdded: " + itemId);
|
||||
this._add(itemId, guid);
|
||||
this._add(folder, parentGuid);
|
||||
this._upScore();
|
||||
},
|
||||
|
||||
onItemRemoved: function (itemId, parentId, index, type, uri,
|
||||
@ -1228,47 +1151,8 @@ BookmarksTracker.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore changes to tags (folders under the tags folder).
|
||||
if (parentId == PlacesUtils.tagsFolderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let grandParentId = -1;
|
||||
try {
|
||||
grandParentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
|
||||
} catch (ex) {
|
||||
// `getFolderIdForItem` can throw if the item no longer exists, such as
|
||||
// when we've removed a subtree using `removeFolderChildren`.
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore tag items (the actual instance of a tag for a bookmark).
|
||||
if (grandParentId == PlacesUtils.tagsFolderId) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The above checks are incomplete: we can still write tombstones for
|
||||
* items that we don't track, and upload extraneous roots.
|
||||
*
|
||||
* Consider the left pane root: it's a child of the Places root, and has
|
||||
* children and grandchildren. `PlacesUIUtils` can create, delete, and
|
||||
* recreate it as needed. We can't determine ancestors when the root or its
|
||||
* children are deleted, because they've already been removed from the
|
||||
* database when `onItemRemoved` is called. Likewise, we can't check their
|
||||
* "exclude from backup" annos, because they've *also* been removed.
|
||||
*
|
||||
* So, we end up writing tombstones for the left pane queries and left
|
||||
* pane root. For good measure, we'll also upload the Places root, because
|
||||
* it's the parent of the left pane root.
|
||||
*
|
||||
* As a workaround, we can track the parent GUID and reconstruct the item's
|
||||
* ancestry at sync time. This is complicated, and the previous behavior was
|
||||
* already wrong, so we'll wait for bug 1258127 to fix this generally.
|
||||
*/
|
||||
this._log.trace("onItemRemoved: " + itemId);
|
||||
this._add(itemId, guid, /* isTombstone */ true);
|
||||
this._add(parentId, parentGuid);
|
||||
this._upScore();
|
||||
},
|
||||
|
||||
_ensureMobileQuery: function _ensureMobileQuery() {
|
||||
@ -1340,7 +1224,7 @@ BookmarksTracker.prototype = {
|
||||
this._log.trace("onItemChanged: " + itemId +
|
||||
(", " + property + (isAnno? " (anno)" : "")) +
|
||||
(value ? (" = \"" + value + "\"") : ""));
|
||||
this._add(itemId, guid);
|
||||
this._upScore();
|
||||
},
|
||||
|
||||
onItemMoved: function BMT_onItemMoved(itemId, oldParent, oldIndex,
|
||||
@ -1352,15 +1236,7 @@ BookmarksTracker.prototype = {
|
||||
}
|
||||
|
||||
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,
|
||||
PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO, SOURCE_SYNC);
|
||||
this._upScore();
|
||||
},
|
||||
|
||||
onBeginUpdateBatch: function () {
|
||||
@ -1375,21 +1251,34 @@ BookmarksTracker.prototype = {
|
||||
onItemVisited: function () {}
|
||||
};
|
||||
|
||||
// Returns an array of root IDs to recursively query for synced bookmarks.
|
||||
// Items in other roots, including tags and organizer queries, will be
|
||||
// ignored.
|
||||
function getChangeRootIds() {
|
||||
return [
|
||||
PlacesUtils.bookmarksMenuFolderId,
|
||||
PlacesUtils.toolbarFolderId,
|
||||
PlacesUtils.unfiledBookmarksFolderId,
|
||||
PlacesUtils.mobileFolderId,
|
||||
];
|
||||
}
|
||||
|
||||
class BookmarksChangeset extends Changeset {
|
||||
getModifiedTimestamp(id) {
|
||||
let change = this.changes[id];
|
||||
return change ? change.modified : Number.NaN;
|
||||
if (!change || change.synced) {
|
||||
// Pretend the change doesn't exist if we've already synced or
|
||||
// reconciled it.
|
||||
return Number.NaN;
|
||||
}
|
||||
return change.modified;
|
||||
}
|
||||
|
||||
has(id) {
|
||||
return id in this.changes && !this.changes[id].synced;
|
||||
}
|
||||
|
||||
setTombstone(id) {
|
||||
let change = this.changes[id];
|
||||
if (change) {
|
||||
change.tombstone = true;
|
||||
}
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
let change = this.changes[id];
|
||||
if (change) {
|
||||
// Mark the change as synced without removing it from the set. We do this
|
||||
// so that we can update Places in `trackRemainingChanges`.
|
||||
change.synced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,28 @@ function* assertChildGuids(folderGuid, expectedChildGuids, message) {
|
||||
deepEqual(childGuids, expectedChildGuids, message);
|
||||
}
|
||||
|
||||
function* fetchAllSyncIds() {
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
let rows = yield db.executeCached(`
|
||||
WITH RECURSIVE
|
||||
syncedItems(id, guid) AS (
|
||||
SELECT b.id, b.guid FROM moz_bookmarks b
|
||||
WHERE b.guid IN ('menu________', 'toolbar_____', 'unfiled_____',
|
||||
'mobile______')
|
||||
UNION ALL
|
||||
SELECT b.id, b.guid FROM moz_bookmarks b
|
||||
JOIN syncedItems s ON b.parent = s.id
|
||||
)
|
||||
SELECT guid FROM syncedItems`);
|
||||
let syncIds = new Set();
|
||||
for (let row of rows) {
|
||||
let syncId = PlacesSyncUtils.bookmarks.guidToSyncId(
|
||||
row.getResultByName("guid"));
|
||||
syncIds.add(syncId);
|
||||
}
|
||||
return syncIds;
|
||||
}
|
||||
|
||||
add_task(function* test_delete_invalid_roots_from_server() {
|
||||
_("Ensure that we delete the Places and Reading List roots from the server.");
|
||||
|
||||
@ -97,6 +119,7 @@ add_task(function* test_change_during_sync() {
|
||||
let bz_guid = yield PlacesUtils.promiseItemGuid(bz_id);
|
||||
_(`Bugzilla GUID: ${bz_guid}`);
|
||||
|
||||
yield PlacesTestUtils.markBookmarksAsSynced();
|
||||
Svc.Obs.notify("weave:engine:start-tracking");
|
||||
|
||||
try {
|
||||
@ -250,14 +273,15 @@ add_task(function* bad_record_allIDs() {
|
||||
_("Type: " + PlacesUtils.bookmarks.getItemType(badRecordID));
|
||||
|
||||
_("Fetching all IDs.");
|
||||
let all = store.getAllIDs();
|
||||
let all = yield* fetchAllSyncIds();
|
||||
|
||||
_("All IDs: " + JSON.stringify(all));
|
||||
do_check_true("menu" in all);
|
||||
do_check_true("toolbar" in all);
|
||||
_("All IDs: " + JSON.stringify([...all]));
|
||||
do_check_true(all.has("menu"));
|
||||
do_check_true(all.has("toolbar"));
|
||||
|
||||
_("Clean up.");
|
||||
PlacesUtils.bookmarks.removeItem(badRecordID);
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(r => server.stop(r));
|
||||
});
|
||||
|
||||
@ -335,6 +359,7 @@ add_task(function* test_processIncoming_error_orderChildren() {
|
||||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
}
|
||||
});
|
||||
@ -407,12 +432,12 @@ add_task(function* test_restorePromptsReupload() {
|
||||
yield BookmarkJSONUtils.importFromFile(backupFile, true);
|
||||
|
||||
_("Ensure we have the bookmarks we expect locally.");
|
||||
let guids = store.getAllIDs();
|
||||
_("GUIDs: " + JSON.stringify(guids));
|
||||
let guids = yield* fetchAllSyncIds();
|
||||
_("GUIDs: " + JSON.stringify([...guids]));
|
||||
let found = false;
|
||||
let count = 0;
|
||||
let newFX;
|
||||
for (let guid in guids) {
|
||||
for (let guid of guids) {
|
||||
count++;
|
||||
let id = store.idForGUID(guid, true);
|
||||
// Only one bookmark, so _all_ should be Firefox!
|
||||
@ -466,9 +491,8 @@ add_task(function* test_restorePromptsReupload() {
|
||||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
yield deferred.promise;
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(r => server.stop(r));
|
||||
}
|
||||
});
|
||||
|
||||
@ -547,6 +571,7 @@ add_task(function* test_mismatched_types() {
|
||||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(r => server.stop(r));
|
||||
}
|
||||
});
|
||||
@ -600,6 +625,7 @@ add_task(function* test_bookmark_guidMap_fail() {
|
||||
do_check_eq(err, "Nooo");
|
||||
|
||||
PlacesUtils.promiseBookmarksTree = pbt;
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(r => server.stop(r));
|
||||
});
|
||||
|
||||
@ -712,6 +738,7 @@ add_task(function* test_misreconciled_root() {
|
||||
do_check_eq(parentGUIDBefore, parentGUIDAfter);
|
||||
do_check_eq(parentIDBefore, parentIDAfter);
|
||||
|
||||
yield PlacesSyncUtils.bookmarks.reset();
|
||||
yield new Promise(r => server.stop(r));
|
||||
});
|
||||
|
||||
|
@ -440,6 +440,8 @@ function assertDeleted(id) {
|
||||
|
||||
add_task(function* test_delete_buffering() {
|
||||
store.wipe();
|
||||
yield PlacesTestUtils.markBookmarksAsSynced();
|
||||
|
||||
try {
|
||||
_("Create a folder with two bookmarks.");
|
||||
let folder = new BookmarkFolder("bookmarks", "testfolder-1");
|
||||
|
@ -2,13 +2,19 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
|
||||
const {
|
||||
// `fetchGuidsWithAnno` isn't exported, but we can still access it here via a
|
||||
// backstage pass.
|
||||
fetchGuidsWithAnno,
|
||||
} = Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://testing-common/PlacesTestUtils.jsm");
|
||||
Cu.import("resource:///modules/PlacesUIUtils.jsm");
|
||||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
@ -23,13 +29,13 @@ const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
|
||||
// Test helpers.
|
||||
function* verifyTrackerEmpty() {
|
||||
let changes = engine.pullNewChanges();
|
||||
equal(changes.count(), 0);
|
||||
let changes = yield tracker.promiseChangedIDs();
|
||||
deepEqual(changes, {});
|
||||
equal(tracker.score, 0);
|
||||
}
|
||||
|
||||
function* resetTracker() {
|
||||
tracker.clearChangedIDs();
|
||||
yield PlacesTestUtils.markBookmarksAsSynced();
|
||||
tracker.resetScore();
|
||||
}
|
||||
|
||||
@ -43,6 +49,7 @@ function* cleanup() {
|
||||
// after this is called (ie, things already tracked should be discarded.)
|
||||
function* startTracking() {
|
||||
Svc.Obs.notify("weave:engine:start-tracking");
|
||||
yield PlacesTestUtils.markBookmarksAsSynced();
|
||||
}
|
||||
|
||||
function* stopTracking() {
|
||||
@ -50,12 +57,12 @@ function* stopTracking() {
|
||||
}
|
||||
|
||||
function* verifyTrackedItems(tracked) {
|
||||
let changes = engine.pullNewChanges();
|
||||
let trackedIDs = new Set(changes.ids());
|
||||
let changedIDs = yield tracker.promiseChangedIDs();
|
||||
let trackedIDs = new Set(Object.keys(changedIDs));
|
||||
for (let guid of tracked) {
|
||||
ok(changes.has(guid), `${guid} should be tracked`);
|
||||
ok(changes.getModifiedTimestamp(guid) > 0,
|
||||
`${guid} should have a modified time`);
|
||||
ok(guid in changedIDs, `${guid} should be tracked`);
|
||||
ok(changedIDs[guid].modified > 0, `${guid} should have a modified time`);
|
||||
ok(changedIDs[guid].counter >= -1, `${guid} should have a change counter`);
|
||||
trackedIDs.delete(guid);
|
||||
}
|
||||
equal(trackedIDs.size, 0, `Unhandled tracked IDs: ${
|
||||
@ -63,23 +70,78 @@ function* verifyTrackedItems(tracked) {
|
||||
}
|
||||
|
||||
function* verifyTrackedCount(expected) {
|
||||
let changes = engine.pullNewChanges();
|
||||
equal(changes.count(), expected);
|
||||
let changedIDs = yield tracker.promiseChangedIDs();
|
||||
do_check_attribute_count(changedIDs, expected);
|
||||
}
|
||||
|
||||
// Copied from PlacesSyncUtils.jsm.
|
||||
function findAnnoItems(anno, val) {
|
||||
let annos = PlacesUtils.annotations;
|
||||
return annos.getItemsWithAnnotation(anno, {}).filter(id =>
|
||||
annos.getItemAnnotation(id, anno) == val);
|
||||
// A debugging helper that dumps the full bookmarks tree.
|
||||
function* dumpBookmarks() {
|
||||
let columns = ["id", "title", "guid", "syncStatus", "syncChangeCounter", "position"];
|
||||
return PlacesUtils.promiseDBConnection().then(connection => {
|
||||
let all = [];
|
||||
return connection.executeCached(`SELECT ${columns.join(", ")} FROM moz_bookmarks;`,
|
||||
{},
|
||||
row => {
|
||||
let repr = {};
|
||||
for (let column of columns) {
|
||||
repr[column] = row.getResultByName(column);
|
||||
}
|
||||
all.push(repr);
|
||||
}
|
||||
).then(() => {
|
||||
dump("All bookmarks:\n");
|
||||
dump(JSON.stringify(all, undefined, 2));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
var populateTree = Task.async(function* populate(parentId, ...items) {
|
||||
let guids = {};
|
||||
for (let item of items) {
|
||||
let itemId;
|
||||
switch (item.type) {
|
||||
case PlacesUtils.bookmarks.TYPE_BOOKMARK:
|
||||
itemId = PlacesUtils.bookmarks.insertBookmark(parentId,
|
||||
Utils.makeURI(item.url),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, item.title);
|
||||
break;
|
||||
|
||||
case PlacesUtils.bookmarks.TYPE_FOLDER: {
|
||||
itemId = PlacesUtils.bookmarks.createFolder(parentId,
|
||||
item.title, PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
Object.assign(guids, yield* populate(itemId, ...item.children));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported item type: ${item.type}`);
|
||||
}
|
||||
if (item.exclude) {
|
||||
PlacesUtils.annotations.setItemAnnotation(
|
||||
itemId, BookmarkAnnos.EXCLUDEBACKUP_ANNO, "Don't back this up", 0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
}
|
||||
guids[item.title] = yield PlacesUtils.promiseItemGuid(itemId);
|
||||
}
|
||||
return guids;
|
||||
});
|
||||
|
||||
add_task(function* test_tracking() {
|
||||
_("Test starting and stopping the tracker");
|
||||
|
||||
// Remove existing tracking information for roots.
|
||||
yield startTracking();
|
||||
|
||||
let folder = PlacesUtils.bookmarks.createFolder(
|
||||
PlacesUtils.bookmarks.bookmarksMenuFolder,
|
||||
"Test Folder", PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
|
||||
// creating the folder should have made 2 changes - the folder itself and
|
||||
// the parent of the folder.
|
||||
yield verifyTrackedCount(2);
|
||||
// Reset the changes as the rest of the test doesn't want to see these.
|
||||
yield resetTracker();
|
||||
|
||||
function createBmk() {
|
||||
return PlacesUtils.bookmarks.insertBookmark(
|
||||
folder, Utils.makeURI("http://getfirefox.com"),
|
||||
@ -87,37 +149,18 @@ add_task(function* test_tracking() {
|
||||
}
|
||||
|
||||
try {
|
||||
_("Create bookmark. Won't show because we haven't started tracking yet");
|
||||
createBmk();
|
||||
yield verifyTrackedCount(0);
|
||||
do_check_eq(tracker.score, 0);
|
||||
|
||||
_("Tell the tracker to start tracking changes.");
|
||||
yield startTracking();
|
||||
createBmk();
|
||||
// We expect two changed items because the containing folder
|
||||
// changed as well (new child).
|
||||
yield verifyTrackedCount(2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
_("Notifying twice won't do any harm.");
|
||||
yield startTracking();
|
||||
createBmk();
|
||||
yield verifyTrackedCount(3);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
|
||||
|
||||
_("Let's stop tracking again.");
|
||||
yield resetTracker();
|
||||
yield stopTracking();
|
||||
createBmk();
|
||||
yield verifyTrackedCount(0);
|
||||
do_check_eq(tracker.score, 0);
|
||||
|
||||
_("Notifying twice won't do any harm.");
|
||||
yield stopTracking();
|
||||
createBmk();
|
||||
yield verifyTrackedCount(0);
|
||||
do_check_eq(tracker.score, 0);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
@ -205,6 +248,11 @@ add_task(function* test_tracker_sql_batching() {
|
||||
|
||||
do_check_eq(createdIDs.length, numItems);
|
||||
yield verifyTrackedCount(numItems + 1); // the folder is also tracked.
|
||||
yield resetTracker();
|
||||
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.unfiledBookmarksFolder);
|
||||
yield verifyTrackedCount(numItems + 1);
|
||||
|
||||
yield cleanup();
|
||||
});
|
||||
|
||||
@ -220,7 +268,7 @@ add_task(function* test_onItemAdded() {
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
let syncFolderGUID = engine._store.GUIDForId(syncFolderID);
|
||||
yield verifyTrackedItems(["menu", syncFolderGUID]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
yield resetTracker();
|
||||
yield startTracking();
|
||||
@ -232,7 +280,7 @@ add_task(function* test_onItemAdded() {
|
||||
"Sync Bookmark");
|
||||
let syncBmkGUID = engine._store.GUIDForId(syncBmkID);
|
||||
yield verifyTrackedItems([syncFolderGUID, syncBmkGUID]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
yield resetTracker();
|
||||
yield startTracking();
|
||||
@ -243,7 +291,7 @@ add_task(function* test_onItemAdded() {
|
||||
PlacesUtils.bookmarks.getItemIndex(syncFolderID));
|
||||
let syncSepGUID = engine._store.GUIDForId(syncSepID);
|
||||
yield verifyTrackedItems(["menu", syncSepGUID]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -263,7 +311,7 @@ add_task(function* test_async_onItemAdded() {
|
||||
title: "Async Folder",
|
||||
});
|
||||
yield verifyTrackedItems(["menu", asyncFolder.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
yield resetTracker();
|
||||
yield startTracking();
|
||||
@ -276,7 +324,7 @@ add_task(function* test_async_onItemAdded() {
|
||||
title: "Async Bookmark",
|
||||
});
|
||||
yield verifyTrackedItems([asyncFolder.guid, asyncBmk.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
yield resetTracker();
|
||||
yield startTracking();
|
||||
@ -288,7 +336,7 @@ add_task(function* test_async_onItemAdded() {
|
||||
index: asyncFolder.index,
|
||||
});
|
||||
yield verifyTrackedItems(["menu", asyncSep.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -429,7 +477,7 @@ add_task(function* test_onItemTagged() {
|
||||
|
||||
// bookmark should be tracked, folder should not be.
|
||||
yield verifyTrackedItems([bGUID]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 5);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -461,7 +509,7 @@ add_task(function* test_onItemUntagged() {
|
||||
PlacesUtils.tagging.untagURI(uri, ["foo"]);
|
||||
|
||||
yield verifyTrackedItems([fx1GUID, fx2GUID]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -504,7 +552,7 @@ add_task(function* test_async_onItemUntagged() {
|
||||
yield PlacesUtils.bookmarks.remove(fxTag.guid);
|
||||
|
||||
yield verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -562,7 +610,7 @@ add_task(function* test_async_onItemTagged() {
|
||||
});
|
||||
|
||||
yield verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 6);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -697,7 +745,8 @@ add_task(function* test_onItemPostDataChanged() {
|
||||
// PlacesTransactions.NewBookmark.
|
||||
_("Post data for the bookmark should be ignored");
|
||||
yield PlacesUtils.setPostDataForBookmark(fx_id, "postData");
|
||||
yield verifyTrackerEmpty();
|
||||
yield verifyTrackedItems([]);
|
||||
do_check_eq(tracker.score, 0);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -773,9 +822,7 @@ add_task(function* test_onItemAdded_filtered_root() {
|
||||
|
||||
_("New root and bookmark should be ignored");
|
||||
yield verifyTrackedItems([]);
|
||||
// ...But we'll still increment the score and filter out the changes at
|
||||
// sync time.
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 6);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -783,7 +830,7 @@ add_task(function* test_onItemAdded_filtered_root() {
|
||||
});
|
||||
|
||||
add_task(function* test_onItemDeleted_filtered_root() {
|
||||
_("Deleted items outside the change roots should be tracked");
|
||||
_("Deleted items outside the change roots should not be tracked");
|
||||
|
||||
try {
|
||||
yield stopTracking();
|
||||
@ -800,13 +847,9 @@ add_task(function* test_onItemDeleted_filtered_root() {
|
||||
|
||||
PlacesUtils.bookmarks.removeItem(rootBmkID);
|
||||
|
||||
// We shouldn't upload tombstones for items in filtered roots, but the
|
||||
// `onItemRemoved` observer doesn't have enough context to determine
|
||||
// the root, so we'll end up uploading it.
|
||||
yield verifyTrackedItems([rootBmkGUID]);
|
||||
// We'll increment the counter twice (once for the removed item, and once
|
||||
// for the Places root), then filter out the root.
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
yield verifyTrackedItems([]);
|
||||
// We'll still increment the counter for the removed item.
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -832,13 +875,15 @@ add_task(function* test_onPageAnnoChanged() {
|
||||
_("Add a page annotation");
|
||||
PlacesUtils.annotations.setPageAnnotation(pageURI, "URIProperties/characterSet",
|
||||
"UTF-8", 0, PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
yield verifyTrackerEmpty();
|
||||
yield verifyTrackedItems([]);
|
||||
do_check_eq(tracker.score, 0);
|
||||
yield resetTracker();
|
||||
|
||||
_("Remove the page annotation");
|
||||
PlacesUtils.annotations.removePageAnnotation(pageURI,
|
||||
"URIProperties/characterSet");
|
||||
yield verifyTrackerEmpty();
|
||||
yield verifyTrackedItems([]);
|
||||
do_check_eq(tracker.score, 0);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -877,7 +922,8 @@ add_task(function* test_onFaviconChanged() {
|
||||
},
|
||||
Services.scriptSecurityManager.getSystemPrincipal());
|
||||
});
|
||||
yield verifyTrackerEmpty();
|
||||
yield verifyTrackedItems([]);
|
||||
do_check_eq(tracker.score, 0);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -901,9 +947,9 @@ add_task(function* test_onLivemarkAdded() {
|
||||
livemark.terminate();
|
||||
|
||||
yield verifyTrackedItems(["menu", livemark.guid]);
|
||||
// Three changes: one for the parent, one for creating the livemark
|
||||
// folder, and one for setting the "livemark/feedURI" anno on the folder.
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
// Two observer notifications: one for creating the livemark folder, and
|
||||
// one for setting the "livemark/feedURI" anno on the folder.
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -931,7 +977,7 @@ add_task(function* test_onLivemarkDeleted() {
|
||||
});
|
||||
|
||||
yield verifyTrackedItems(["menu", livemark.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -965,13 +1011,14 @@ add_task(function* test_onItemMoved() {
|
||||
yield verifyTrackedItems(['menu']);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
yield resetTracker();
|
||||
yield PlacesTestUtils.markBookmarksAsSynced();
|
||||
|
||||
// Moving a bookmark to a different folder will track the old
|
||||
// folder, the new folder and the bookmark.
|
||||
PlacesUtils.bookmarks.moveItem(fx_id, PlacesUtils.bookmarks.toolbarFolder,
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX);
|
||||
yield verifyTrackedItems(['menu', 'toolbar', fx_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
@ -1017,7 +1064,7 @@ add_task(function* test_async_onItemMoved_update() {
|
||||
index: PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
});
|
||||
yield verifyTrackedItems(['menu', 'toolbar', tbBmk.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1170,7 +1217,7 @@ add_task(function* test_onItemDeleted_removeFolderTransaction() {
|
||||
_("Execute the remove folder transaction");
|
||||
txn.doTransaction();
|
||||
yield verifyTrackedItems(["menu", folder_guid, fx_guid, tb_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 6);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
yield resetTracker();
|
||||
|
||||
_("Undo the remove folder transaction");
|
||||
@ -1180,13 +1227,13 @@ add_task(function* test_onItemDeleted_removeFolderTransaction() {
|
||||
let new_folder_guid = yield PlacesUtils.promiseItemGuid(folder_id);
|
||||
|
||||
yield verifyTrackedItems(["menu", new_folder_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
yield resetTracker();
|
||||
|
||||
_("Redo the transaction");
|
||||
txn.redoTransaction();
|
||||
yield verifyTrackedItems(["menu", new_folder_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1232,7 +1279,7 @@ add_task(function* test_treeMoved() {
|
||||
folder2_id, PlacesUtils.bookmarks.bookmarksMenuFolder, 0);
|
||||
// the menu and both folders should be tracked, the children should not be.
|
||||
yield verifyTrackedItems(['menu', folder1_guid, folder2_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1262,7 +1309,7 @@ add_task(function* test_onItemDeleted() {
|
||||
PlacesUtils.bookmarks.removeItem(tb_id);
|
||||
|
||||
yield verifyTrackedItems(['menu', tb_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1294,7 +1341,7 @@ add_task(function* test_async_onItemDeleted() {
|
||||
yield PlacesUtils.bookmarks.remove(fxBmk.guid);
|
||||
|
||||
yield verifyTrackedItems(["menu", fxBmk.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1363,18 +1410,22 @@ add_task(function* test_async_onItemDeleted_eraseEverything() {
|
||||
_(`Bugs grandchild GUID: ${bugsGrandChildBmk.guid}`);
|
||||
|
||||
yield startTracking();
|
||||
|
||||
// Simulate moving a synced item into a new folder. Deleting the folder
|
||||
// should write a tombstone for the item, but not the folder.
|
||||
yield PlacesTestUtils.setBookmarkSyncFields({
|
||||
guid: bugsChildFolder.guid,
|
||||
syncStatus: PlacesUtils.bookmarks.SYNC_STATUS.NEW,
|
||||
});
|
||||
yield PlacesUtils.bookmarks.eraseEverything();
|
||||
|
||||
// `eraseEverything` removes all items from the database before notifying
|
||||
// observers. Because of this, grandchild lookup in the tracker's
|
||||
// `onItemRemoved` observer will fail. That means we won't track
|
||||
// (bzBmk.guid, bugsGrandChildBmk.guid, bugsChildFolder.guid), even
|
||||
// though we should.
|
||||
// bugsChildFolder's sync status is still "NEW", so it shouldn't be
|
||||
// tracked. bugsGrandChildBmk is "NORMAL", so we *should* write a
|
||||
// tombstone and track it.
|
||||
yield verifyTrackedItems(["menu", mozBmk.guid, mdnBmk.guid, "toolbar",
|
||||
bugsFolder.guid, "mobile", fxBmk.guid,
|
||||
tbBmk.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 10);
|
||||
tbBmk.guid, "unfiled", bzBmk.guid,
|
||||
bugsGrandChildBmk.guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 8);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1416,7 +1467,7 @@ add_task(function* test_onItemDeleted_removeFolderChildren() {
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.mobileFolderId);
|
||||
|
||||
yield verifyTrackedItems(["mobile", fx_guid, tb_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 4);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 2);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1461,7 +1512,7 @@ add_task(function* test_onItemDeleted_tree() {
|
||||
PlacesUtils.bookmarks.removeItem(folder2_id);
|
||||
|
||||
yield verifyTrackedItems([fx_guid, tb_guid, folder1_guid, folder2_guid]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 6);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 3);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
@ -1472,30 +1523,34 @@ add_task(function* test_mobile_query() {
|
||||
_("Ensure we correctly create the mobile query");
|
||||
|
||||
try {
|
||||
yield startTracking();
|
||||
|
||||
// Creates the organizer queries as a side effect.
|
||||
let leftPaneId = PlacesUIUtils.leftPaneFolderId;
|
||||
_(`Left pane root ID: ${leftPaneId}`);
|
||||
|
||||
let allBookmarksIds = findAnnoItems("PlacesOrganizer/OrganizerQuery", "AllBookmarks");
|
||||
equal(allBookmarksIds.length, 1, "Should create folder with all bookmarks queries");
|
||||
let allBookmarkGuid = yield PlacesUtils.promiseItemGuid(allBookmarksIds[0]);
|
||||
let allBookmarksGuids = yield fetchGuidsWithAnno("PlacesOrganizer/OrganizerQuery",
|
||||
"AllBookmarks");
|
||||
equal(allBookmarksGuids.length, 1, "Should create folder with all bookmarks queries");
|
||||
let allBookmarkGuid = allBookmarksGuids[0];
|
||||
|
||||
_("Try creating query after organizer is ready");
|
||||
tracker._ensureMobileQuery();
|
||||
let queryIds = findAnnoItems("PlacesOrganizer/OrganizerQuery", "MobileBookmarks");
|
||||
equal(queryIds.length, 0, "Should not create query without any mobile bookmarks");
|
||||
let queryGuids = yield fetchGuidsWithAnno("PlacesOrganizer/OrganizerQuery",
|
||||
"MobileBookmarks");
|
||||
equal(queryGuids.length, 0, "Should not create query without any mobile bookmarks");
|
||||
|
||||
_("Insert mobile bookmark, then create query");
|
||||
yield PlacesUtils.bookmarks.insert({
|
||||
let mozBmk = yield PlacesUtils.bookmarks.insert({
|
||||
parentGuid: PlacesUtils.bookmarks.mobileGuid,
|
||||
url: "https://mozilla.org",
|
||||
});
|
||||
tracker._ensureMobileQuery();
|
||||
queryIds = findAnnoItems("PlacesOrganizer/OrganizerQuery", "MobileBookmarks", {});
|
||||
equal(queryIds.length, 1, "Should create query once mobile bookmarks exist");
|
||||
queryGuids = yield fetchGuidsWithAnno("PlacesOrganizer/OrganizerQuery",
|
||||
"MobileBookmarks");
|
||||
equal(queryGuids.length, 1, "Should create query once mobile bookmarks exist");
|
||||
|
||||
let queryId = queryIds[0];
|
||||
let queryGuid = yield PlacesUtils.promiseItemGuid(queryId);
|
||||
let queryGuid = queryGuids[0];
|
||||
|
||||
let queryInfo = yield PlacesUtils.bookmarks.fetch(queryGuid);
|
||||
equal(queryInfo.url, `place:folder=${PlacesUtils.mobileFolderId}`, "Query should point to mobile root");
|
||||
@ -1528,8 +1583,8 @@ add_task(function* test_mobile_query() {
|
||||
"Should fix query URL to point to mobile root");
|
||||
|
||||
_("We shouldn't track the query or the left pane root");
|
||||
yield verifyTrackedCount(0);
|
||||
do_check_eq(tracker.score, 0);
|
||||
yield verifyTrackedItems([mozBmk.guid, "mobile"]);
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_XLARGE * 5);
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
yield cleanup();
|
||||
|
Loading…
Reference in New Issue
Block a user