Bug 626796: Bookmark sync: restore from backup should trigger reupload. r=philiKON

This commit is contained in:
Richard Newman 2011-01-24 23:06:42 -08:00
parent 6a1fddc2f0
commit bdc5390864
3 changed files with 190 additions and 23 deletions

View File

@ -63,6 +63,7 @@ Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/main.js"); // For access to Service.
function PlacesItem(collection, id, type) {
CryptoWrapper.call(this, collection, id);
@ -236,7 +237,6 @@ let kSpecialIds = {
function BookmarksEngine() {
SyncEngine.call(this, "Bookmarks");
this._handleImport();
}
BookmarksEngine.prototype = {
__proto__: SyncEngine.prototype,
@ -245,26 +245,6 @@ BookmarksEngine.prototype = {
_trackerObj: BookmarksTracker,
version: 2,
_handleImport: function _handleImport() {
Svc.Obs.add("bookmarks-restore-begin", function() {
this._log.debug("Ignoring changes from importing bookmarks");
this._tracker.ignoreAll = true;
}, this);
Svc.Obs.add("bookmarks-restore-success", function() {
this._log.debug("Tracking all items on successful import");
this._tracker.ignoreAll = false;
// Mark all the items as changed so they get uploaded
for (let id in this._store.getAllIDs())
this._tracker.addChangedID(id);
}, this);
Svc.Obs.add("bookmarks-restore-failed", function() {
this._tracker.ignoreAll = false;
}, this);
},
_sync: Utils.batchSync("Bookmark", SyncEngine),
_syncStartup: function _syncStart() {
@ -1286,12 +1266,18 @@ BookmarksTracker.prototype = {
case "weave:engine:start-tracking":
if (!this._enabled) {
Svc.Bookmark.addObserver(this, true);
Svc.Obs.add("bookmarks-restore-begin", this);
Svc.Obs.add("bookmarks-restore-success", this);
Svc.Obs.add("bookmarks-restore-failed", this);
this._enabled = true;
}
break;
case "weave:engine:stop-tracking":
if (this._enabled) {
Svc.Bookmark.removeObserver(this);
Svc.Obs.remove("bookmarks-restore-begin", this);
Svc.Obs.remove("bookmarks-restore-success", this);
Svc.Obs.remove("bookmarks-restore-failed", this);
this._enabled = false;
}
// Fall through to clean up.
@ -1301,6 +1287,24 @@ BookmarksTracker.prototype = {
this.__ls = null;
this.__bms = null;
break;
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.");
Weave.Service.resetClient([this.name]);
Weave.Service.wipeServer([this.name]);
Weave.Service.prepCommand("wipeEngine", [this.name]);
break;
case "bookmarks-restore-failed":
this._log.debug("Tracking all items on failed import.");
this.ignoreAll = false;
break;
}
},

View File

@ -123,9 +123,13 @@ ServerWBO.prototype = {
*
* Note that if you want these records to be accessible individually,
* you need to register their handlers with the server separately!
*
* Passing `true` for acceptNew will allow POSTs of new WBOs to this
* collection. New WBOs will be created and wired in on the fly.
*/
function ServerCollection(wbos) {
function ServerCollection(wbos, acceptNew) {
this.wbos = wbos || {};
this.acceptNew = acceptNew || false;
}
ServerCollection.prototype = {
@ -139,7 +143,9 @@ ServerCollection.prototype = {
let result;
if (options.full) {
let data = [wbo.get() for ([id, wbo] in Iterator(this.wbos))
if (this._inResultSet(wbo, options))];
// Drop deleted.
if (wbo.modified &&
this._inResultSet(wbo, options))];
if (options.limit) {
data = data.slice(0, options.limit);
}
@ -165,6 +171,11 @@ ServerCollection.prototype = {
// registered with us as successful and all other records as failed.
for each (let record in input) {
let wbo = this.wbos[record.id];
if (!wbo && this.acceptNew) {
_("Creating WBO " + JSON.stringify(record.id) + " on the fly.");
wbo = new ServerWBO(record.id);
this.wbos[record.id] = wbo;
}
if (wbo) {
wbo.payload = record.payload;
wbo.modified = Date.now() / 1000;
@ -181,6 +192,7 @@ ServerCollection.prototype = {
delete: function(options) {
for (let [id, wbo] in Iterator(this.wbos)) {
if (this._inResultSet(wbo, options)) {
_("Deleting " + JSON.stringify(wbo));
wbo.delete();
}
}

View File

@ -1,8 +1,19 @@
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/engines/bookmarks.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/service.js");
try {
Cu.import("resource://gre/modules/PlacesUtils.jsm");
}
catch(ex) {
Cu.import("resource://gre/modules/utils.js");
}
Engines.register(BookmarksEngine);
function makeEngine() {
return new BookmarksEngine();
}
@ -125,6 +136,145 @@ function test_processIncoming_error_orderChildren() {
}
}
function test_restorePromptsReupload() {
_("Ensure that restoring from a backup will reupload all records.");
Svc.Prefs.set("username", "foo");
Service.serverURL = "http://localhost:8080/";
Service.clusterURL = "http://localhost:8080/";
let collection = new ServerCollection({}, true);
let engine = new BookmarksEngine();
let store = engine._store;
let global = new ServerWBO('global',
{engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}});
let server = httpd_setup({
"/1.0/foo/storage/meta/global": global.handler(),
"/1.0/foo/storage/bookmarks": collection.handler()
});
Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
try {
let folder1_id = Svc.Bookmark.createFolder(
Svc.Bookmark.toolbarFolder, "Folder 1", 0);
let folder1_guid = store.GUIDForId(folder1_id);
_("Folder 1: " + folder1_id + ", " + folder1_guid);
let fxuri = Utils.makeURI("http://getfirefox.com/");
let tburi = Utils.makeURI("http://getthunderbird.com/");
_("Create a single record.");
let bmk1_id = Svc.Bookmark.insertBookmark(
folder1_id, fxuri, Svc.Bookmark.DEFAULT_INDEX, "Get Firefox!");
let bmk1_guid = store.GUIDForId(bmk1_id);
_("Get Firefox!: " + bmk1_id + ", " + bmk1_guid);
let dirSvc = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties);
let backupFile = dirSvc.get("TmpD", Ci.nsILocalFile);
_("Make a backup.");
backupFile.append("t_b_e_" + Date.now() + ".json");
_("Backing up to file " + backupFile.path);
backupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
PlacesUtils.backupBookmarksToFile(backupFile);
_("Create a different record and sync.");
let bmk2_id = Svc.Bookmark.insertBookmark(
folder1_id, tburi, Svc.Bookmark.DEFAULT_INDEX, "Get Thunderbird!");
let bmk2_guid = store.GUIDForId(bmk2_id);
_("Get Thunderbird!: " + bmk2_id + ", " + bmk2_guid);
Svc.Bookmark.removeItem(bmk1_id);
let error;
try {
engine.sync();
} catch(ex) {
error = ex;
_("Got error: " + Utils.exceptionStr(ex));
}
do_check_true(!error);
_("Verify that there's only one bookmark on the server, and it's Thunderbird.");
// Of course, there's also the Bookmarks Toolbar and Bookmarks Menu...
let wbos = [id for ([id, wbo] in Iterator(collection.wbos))
if (["menu", "toolbar", "mobile", folder1_guid].indexOf(id) == -1)];
do_check_eq(wbos.length, 1);
do_check_eq(wbos[0], bmk2_guid);
_("Now restore from a backup.");
PlacesUtils.restoreBookmarksFromJSONFile(backupFile);
_("Ensure we have the bookmarks we expect locally.");
let guids = store.getAllIDs();
_("GUIDs: " + JSON.stringify(guids));
let found = false;
let count = 0;
let newFX;
for (let guid in guids) {
count++;
let id = store.idForGUID(guid, true);
// Only one bookmark, so _all_ should be Firefox!
if (Svc.Bookmark.getItemType(id) == Svc.Bookmark.TYPE_BOOKMARK) {
let uri = Svc.Bookmark.getBookmarkURI(id);
_("Found URI " + uri.spec + " for GUID " + guid);
do_check_eq(uri.spec, fxuri.spec);
newFX = guid; // Save the new GUID after restore.
found = true; // Only runs if the above check passes.
}
}
_("We found it: " + found);
do_check_true(found);
_("Have the correct number of IDs locally, too.");
do_check_eq(count, ["menu", "toolbar", folder1_id, bmk1_id].length);
_("Sync again. This'll wipe bookmarks from the server.");
try {
engine.sync();
} catch(ex) {
error = ex;
_("Got error: " + Utils.exceptionStr(ex));
}
do_check_true(!error);
_("Verify that there's only one bookmark on the server, and it's Firefox.");
// Of course, there's also the Bookmarks Toolbar and Bookmarks Menu...
wbos = [JSON.parse(JSON.parse(wbo.payload).ciphertext)
for ([id, wbo] in Iterator(collection.wbos))
if (wbo.payload)];
_("WBOs: " + JSON.stringify(wbos));
let bookmarks = [wbo for each (wbo in wbos) if (wbo.type == "bookmark")];
do_check_eq(bookmarks.length, 1);
do_check_eq(bookmarks[0].id, newFX);
do_check_eq(bookmarks[0].bmkUri, fxuri.spec);
do_check_eq(bookmarks[0].title, "Get Firefox!");
_("Our old friend Folder 1 is still in play.");
let folders = [wbo for each (wbo in wbos)
if ((wbo.type == "folder") &&
(wbo.id != "menu") &&
(wbo.id != "toolbar"))];
do_check_eq(folders.length, 1);
do_check_eq(folders[0].title, "Folder 1");
} finally {
store.wipe();
server.stop(do_test_finished);
Svc.Prefs.resetBranch("");
Records.clearCache();
syncTesting = new SyncTestingInfrastructure(makeEngine);
}
}
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Engine.Bookmarks").level = Log4Moz.Level.Trace;
@ -133,4 +283,5 @@ function run_test() {
test_processIncoming_error_orderChildren();
test_ID_caching();
test_restorePromptsReupload();
}