Bug 1271799 - implement undo functionality in automigration code, r=mak

MozReview-Commit-ID: 28yvZyVimOx

--HG--
extra : rebase_source : 216da87f46d1bb7cde17507431b04b8bbc3367c3
This commit is contained in:
Gijs Kruitbosch 2016-06-20 12:12:28 +01:00
parent 5da90b343c
commit a580940405
2 changed files with 197 additions and 5 deletions

View File

@ -8,13 +8,19 @@ this.EXPORTED_SYMBOLS = ["AutoMigrate"];
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
const kAutoMigrateStartedPref = "browser.migrate.automigrate-started";
const kAutoMigrateFinishedPref = "browser.migrate.automigrate-finished";
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const AutoMigrate = {
get resourceTypesToUse() {
let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
return BOOKMARKS | HISTORY | FORMDATA | PASSWORDS;
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
return BOOKMARKS | HISTORY | PASSWORDS;
},
/**
@ -47,11 +53,13 @@ const AutoMigrate = {
histogram.add(sawErrors ? "finished-with-errors" : "finished");
Services.obs.removeObserver(migrationObserver, "Migration:Ended");
Services.obs.removeObserver(migrationObserver, "Migration:ItemError");
Services.prefs.setCharPref(kAutoMigrateFinishedPref, Date.now().toString());
}
};
Services.obs.addObserver(migrationObserver, "Migration:Ended", false);
Services.obs.addObserver(migrationObserver, "Migration:ItemError", false);
Services.prefs.setCharPref(kAutoMigrateStartedPref, Date.now().toString());
migrator.migrate(this.resourceTypesToUse, profileStartup, profileToMigrate);
histogram.add("migrate-called-without-exceptions");
},
@ -109,5 +117,59 @@ const AutoMigrate = {
}
return profiles ? profiles[0].id : null;
},
getUndoRange() {
let start, finish;
try {
start = parseInt(Services.prefs.getCharPref(kAutoMigrateStartedPref), 10);
finish = parseInt(Services.prefs.getCharPref(kAutoMigrateFinishedPref), 10);
} catch (ex) {
Cu.reportError(ex);
}
if (!finish || !start) {
return null;
}
return [new Date(start), new Date(finish)];
},
canUndo() {
if (!this.getUndoRange()) {
return Promise.resolve(false);
}
// Return a promise resolving to false if we're signed into sync, resolve
// to true otherwise.
let {fxAccounts} = Cu.import("resource://gre/modules/FxAccounts.jsm", {});
return fxAccounts.getSignedInUser().then(user => !user, () => Promise.resolve(true));
},
undo: Task.async(function* () {
if (!(yield this.canUndo())) {
throw new Error("Can't undo!");
}
yield PlacesUtils.bookmarks.eraseEverything();
// NB: we drop the start time of the migration for now. This is because
// imported history will always end up being 'backdated' to the actual
// visit time recorded by the browser from which we imported. As a result,
// a lower bound on this item doesn't really make sense.
// Note that for form data this could be different, but we currently don't
// support form data import from any non-Firefox browser, so it isn't
// imported from other browsers by the automigration code, nor do we
// remove it here.
let range = this.getUndoRange();
yield PlacesUtils.history.removeVisitsByFilter({
beginDate: new Date(0),
endDate: range[1]
});
try {
Services.logins.removeAllLogins();
} catch (ex) {
// ignore failure.
}
Services.prefs.clearUserPref("browser.migrate.automigrate-started");
Services.prefs.clearUserPref("browser.migrate.automigrate-finished");
}),
};

View File

@ -1,9 +1,14 @@
Cu.import("resource:///modules/MigrationUtils.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
Cu.import("resource://testing-common/PlacesTestUtils.jsm");
let AutoMigrateBackstage = Cu.import("resource:///modules/AutoMigrate.jsm");
let gShimmedMigratorKeyPicker = null;
let gShimmedMigrator = null;
const kUsecPerMin = 60 * 1000000;
// This is really a proxy on MigrationUtils, but if we specify that directly,
// we get in trouble because the object itself is frozen, and Proxies can't
// return a different value to an object when directly proxying a frozen
@ -112,8 +117,133 @@ add_task(function* checkIntegration() {
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
"getMigrateData called with 'null' as a profile");
let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | FORMDATA | PASSWORDS;
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
"getMigrateData called with 'null' as a profile");
"migrate called with 'null' as a profile");
});
/**
* Test the undo preconditions and a no-op undo in the automigrator.
*/
add_task(function* checkUndoPreconditions() {
gShimmedMigrator = {
get sourceProfiles() {
do_print("Read sourceProfiles");
return null;
},
getMigrateData(profileToMigrate) {
this._getMigrateDataArgs = profileToMigrate;
return Ci.nsIBrowserProfileMigrator.BOOKMARKS;
},
migrate(types, startup, profileToMigrate) {
this._migrateArgs = [types, startup, profileToMigrate];
TestUtils.executeSoon(function() {
Services.obs.notifyObservers(null, "Migration:Ended", undefined);
});
},
};
gShimmedMigratorKeyPicker = function() {
return "gobbledygook";
};
AutoMigrate.migrate("startup");
let migrationFinishedPromise = TestUtils.topicObserved("Migration:Ended");
Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null,
"getMigrateData called with 'null' as a profile");
let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS;
Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null],
"migrate called with 'null' as a profile");
yield migrationFinishedPromise;
Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-started"),
"Should have set start time pref");
Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-finished"),
"Should have set finish time pref");
Assert.ok((yield AutoMigrate.canUndo()), "Should be able to undo migration");
let [beginRange, endRange] = AutoMigrate.getUndoRange();
let stringRange = `beginRange: ${beginRange}; endRange: ${endRange}`;
Assert.ok(beginRange <= endRange,
"Migration should have started before or when it ended " + stringRange);
yield AutoMigrate.undo();
Assert.ok(true, "Should be able to finish an undo cycle.");
});
/**
* Fake a migration and then try to undo it to verify all data gets removed.
*/
add_task(function* checkUndoRemoval() {
let startTime = "" + Date.now();
Services.prefs.setCharPref("browser.migrate.automigrate-started", startTime);
// Insert a login and check that that worked.
let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
login.init("www.mozilla.org", "http://www.mozilla.org", null, "user", "pass", "userEl", "passEl");
Services.logins.addLogin(login);
let storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
"http://www.mozilla.org", null);
Assert.equal(storedLogins.length, 1, "Should have 1 login");
// Insert a bookmark and check that we have exactly 1 bookmark for that URI.
yield PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: "http://www.example.org/",
title: "Some example bookmark",
});
let bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
Assert.ok(bookmark, "Should have a bookmark before undo");
Assert.equal(bookmark.title, "Some example bookmark", "Should have correct bookmark before undo.");
// Insert 2 history visits - one in the current migration time, one from before.
let now_uSec = Date.now() * 1000;
let visitedURI = Services.io.newURI("http://www.example.com/", null, null);
yield PlacesTestUtils.addVisits([
{uri: visitedURI, visitDate: now_uSec},
{uri: visitedURI, visitDate: now_uSec - 100 * kUsecPerMin},
]);
// Verify that both visits get reported.
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
let query = PlacesUtils.history.getNewQuery();
query.uri = visitedURI;
let visits = PlacesUtils.history.executeQuery(query, opts);
visits.root.containerOpen = true;
Assert.equal(visits.root.childCount, 2, "Should have 2 visits");
// Clean up:
visits.root.containerOpen = false;
// Now set finished pref:
let endTime = "" + Date.now();
Services.prefs.setCharPref("browser.migrate.automigrate-finished", endTime);
// Verify that we can undo, then undo:
Assert.ok(yield AutoMigrate.canUndo(), "Should be possible to undo migration");
yield AutoMigrate.undo();
// Check that the undo removed the history visits:
visits = PlacesUtils.history.executeQuery(query, opts);
visits.root.containerOpen = true;
Assert.equal(visits.root.childCount, 0, "Should have no more visits");
visits.root.containerOpen = false;
// Check that the undo removed the bookmarks:
bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"});
Assert.ok(!bookmark, "Should have no bookmarks after undo");
// Check that the undo removed the passwords:
storedLogins = Services.logins.findLogins({}, "www.mozilla.org",
"http://www.mozilla.org", null);
Assert.equal(storedLogins.length, 0, "Should have no logins");
// Finally check prefs got cleared:
Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-started"),
"Should no longer have pref for migration start time.");
Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-finished"),
"Should no longer have pref for migration finish time.");
});