summaryrefslogtreecommitdiffstats
path: root/browser/components/migration
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/migration')
-rw-r--r--browser/components/migration/.eslintrc.js82
-rw-r--r--browser/components/migration/360seProfileMigrator.js328
-rw-r--r--browser/components/migration/AutoMigrate.jsm670
-rw-r--r--browser/components/migration/BrowserProfileMigrators.manifest33
-rw-r--r--browser/components/migration/ChromeProfileMigrator.js557
-rw-r--r--browser/components/migration/ESEDBReader.jsm590
-rw-r--r--browser/components/migration/EdgeProfileMigrator.js450
-rw-r--r--browser/components/migration/FirefoxProfileMigrator.js255
-rw-r--r--browser/components/migration/IEProfileMigrator.js542
-rw-r--r--browser/components/migration/MSMigrationUtils.jsm889
-rw-r--r--browser/components/migration/MigrationUtils.jsm1118
-rw-r--r--browser/components/migration/ProfileMigrator.js21
-rw-r--r--browser/components/migration/SafariProfileMigrator.js650
-rw-r--r--browser/components/migration/content/aboutWelcomeBack.xhtml82
-rw-r--r--browser/components/migration/content/extra-migration-strings.properties14
-rw-r--r--browser/components/migration/content/migration.js549
-rw-r--r--browser/components/migration/content/migration.xul109
-rw-r--r--browser/components/migration/jar.mn9
-rw-r--r--browser/components/migration/moz.build53
-rw-r--r--browser/components/migration/nsIBrowserProfileMigrator.idl77
-rw-r--r--browser/components/migration/nsIEHistoryEnumerator.cpp119
-rw-r--r--browser/components/migration/nsIEHistoryEnumerator.h37
-rw-r--r--browser/components/migration/nsWindowsMigrationUtils.h36
23 files changed, 0 insertions, 7270 deletions
diff --git a/browser/components/migration/.eslintrc.js b/browser/components/migration/.eslintrc.js
deleted file mode 100644
index 6693f83d0..000000000
--- a/browser/components/migration/.eslintrc.js
+++ /dev/null
@@ -1,82 +0,0 @@
-"use strict";
-
-module.exports = { // eslint-disable-line no-undef
- "extends": [
- "../../.eslintrc.js"
- ],
-
- "globals": {
- "Components": true,
- "dump": true,
- "Iterator": true
- },
-
- "env": { "browser": true },
-
- "rules": {
- "block-scoped-var": "error",
- // "brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
- "comma-dangle": "off",
- "comma-spacing": ["warn", {"before": false, "after": true}],
- "comma-style": ["warn", "last"],
- // "complexity": "warn",
- "consistent-return": "error",
- //"curly": "error",
- "dot-notation": "error",
- "eol-last": "error",
- "indent": ["warn", 2, {"SwitchCase": 1}],
- // "key-spacing": ["warn", {"beforeColon": false, "afterColon": true}],
- "keyword-spacing": "warn",
- "max-nested-callbacks": ["error", 3],
- "new-parens": "error",
- "no-array-constructor": "error",
- "no-cond-assign": "error",
- "no-control-regex": "error",
- "no-debugger": "error",
- "no-delete-var": "error",
- "no-dupe-args": "error",
- "no-dupe-keys": "error",
- "no-duplicate-case": "error",
- "no-else-return": "error",
- "no-eval": "error",
- "no-extend-native": "error",
- // "no-extra-bind": "error",
- "no-extra-boolean-cast": "error",
- "no-extra-semi": "warn",
- "no-fallthrough": ["error", { "commentPattern": ".*[Ii]ntentional(?:ly)?\\s+fall(?:ing)?[\\s-]*through.*" }],
- "no-lonely-if": "error",
- "no-mixed-spaces-and-tabs": "error",
- "no-multi-spaces": "warn",
- "no-multi-str": "warn",
- "no-native-reassign": "error",
- "no-nested-ternary": "error",
- "no-redeclare": "error",
- "no-return-assign": "error",
- "no-self-compare": "error",
- "no-sequences": "error",
- "no-shadow": "warn",
- "no-shadow-restricted-names": "error",
- // "no-spaced-func": "warn",
- "no-throw-literal": "error",
- "no-trailing-spaces": "error",
- "no-undef": "error",
- "no-unneeded-ternary": "error",
- "no-unreachable": "error",
- "no-unused-vars": ["error", { "varsIgnorePattern": "^C[ciur]$" }],
- "no-with": "error",
- "padded-blocks": ["warn", "never"],
- "quotes": ["error", "double", { "avoidEscape": true, "allowTemplateLiterals": true }],
- "semi": ["error", "always", {"omitLastInOneLineBlock": true }],
- "semi-spacing": ["warn", {"before": false, "after": true}],
- "space-before-blocks": ["warn", "always"],
- // "space-before-function-paren": ["warn", "never"],
- "space-in-parens": ["warn", "never"],
- "space-infix-ops": ["warn", {"int32Hint": true}],
- // "space-unary-ops": ["warn", { "words": true, "nonwords": false }],
- "strict": ["error", "global"],
- "use-isnan": "error",
- "valid-typeof": "error",
- "yoda": "error"
- }
-};
-
diff --git a/browser/components/migration/360seProfileMigrator.js b/browser/components/migration/360seProfileMigrator.js
deleted file mode 100644
index 42347d542..000000000
--- a/browser/components/migration/360seProfileMigrator.js
+++ /dev/null
@@ -1,328 +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/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
- "resource://gre/modules/Sqlite.jsm");
-
-const kBookmarksFileName = "360sefav.db";
-
-function copyToTempUTF8File(file, charset) {
- let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Ci.nsIFileInputStream);
- inputStream.init(file, -1, -1, 0);
- let inputStr = NetUtil.readInputStreamToString(
- inputStream, inputStream.available(), { charset });
-
- // Use random to reduce the likelihood of a name collision in createUnique.
- let rand = Math.floor(Math.random() * Math.pow(2, 15));
- let leafName = "mozilla-temp-" + rand;
- let tempUTF8File = FileUtils.getFile(
- "TmpD", ["mozilla-temp-files", leafName], true);
- tempUTF8File.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
-
- let out = FileUtils.openAtomicFileOutputStream(tempUTF8File);
- try {
- let bufferedOut = Cc["@mozilla.org/network/buffered-output-stream;1"]
- .createInstance(Ci.nsIBufferedOutputStream);
- bufferedOut.init(out, 4096);
- try {
- let converterOut = Cc["@mozilla.org/intl/converter-output-stream;1"]
- .createInstance(Ci.nsIConverterOutputStream);
- converterOut.init(bufferedOut, "utf-8", 0, 0x0000);
- try {
- converterOut.writeString(inputStr || "");
- bufferedOut.QueryInterface(Ci.nsISafeOutputStream).finish();
- } finally {
- converterOut.close();
- }
- } finally {
- bufferedOut.close();
- }
- } finally {
- out.close();
- }
-
- return tempUTF8File;
-}
-
-function parseINIStrings(file) {
- let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
- getService(Ci.nsIINIParserFactory);
- let parser = factory.createINIParser(file);
- let obj = {};
- let sections = parser.getSections();
- while (sections.hasMore()) {
- let section = sections.getNext();
- obj[section] = {};
-
- let keys = parser.getKeys(section);
- while (keys.hasMore()) {
- let key = keys.getNext();
- obj[section][key] = parser.getString(section, key);
- }
- }
- return obj;
-}
-
-function getHash(aStr) {
- // return the two-digit hexadecimal code for a byte
- let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
-
- let hasher = Cc["@mozilla.org/security/hash;1"].
- createInstance(Ci.nsICryptoHash);
- hasher.init(Ci.nsICryptoHash.MD5);
- let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
- createInstance(Ci.nsIStringInputStream);
- stringStream.data = aStr;
- hasher.updateFromStream(stringStream, -1);
-
- // convert the binary hash data to a hex string.
- let binary = hasher.finish(false);
- return Array.from(binary, (c, i) => toHexString(binary.charCodeAt(i))).join("").toLowerCase();
-}
-
-function Bookmarks(aProfileFolder) {
- let file = aProfileFolder.clone();
- file.append(kBookmarksFileName);
-
- this._file = file;
-}
-Bookmarks.prototype = {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- get exists() {
- return this._file.exists() && this._file.isReadable();
- },
-
- migrate(aCallback) {
- return Task.spawn(function* () {
- let idToGuid = new Map();
- let folderGuid = PlacesUtils.bookmarks.toolbarGuid;
- if (!MigrationUtils.isStartupMigration) {
- folderGuid =
- yield MigrationUtils.createImportedBookmarksFolder("360se", folderGuid);
- }
- idToGuid.set(0, folderGuid);
-
- let connection = yield Sqlite.openConnection({
- path: this._file.path
- });
-
- try {
- let rows = yield connection.execute(
- `WITH RECURSIVE
- bookmark(id, parent_id, is_folder, title, url, pos) AS (
- VALUES(0, -1, 1, '', '', 0)
- UNION
- SELECT f.id, f.parent_id, f.is_folder, f.title, f.url, f.pos
- FROM tb_fav AS f
- JOIN bookmark AS b ON f.parent_id = b.id
- ORDER BY f.pos ASC
- )
- SELECT id, parent_id, is_folder, title, url FROM bookmark WHERE id`);
-
- for (let row of rows) {
- let id = parseInt(row.getResultByName("id"), 10);
- let parent_id = parseInt(row.getResultByName("parent_id"), 10);
- let is_folder = parseInt(row.getResultByName("is_folder"), 10);
- let title = row.getResultByName("title");
- let url = row.getResultByName("url");
-
- let parentGuid = idToGuid.get(parent_id) || idToGuid.get("fallback");
- if (!parentGuid) {
- parentGuid = PlacesUtils.bookmarks.unfiledGuid;
- if (!MigrationUtils.isStartupMigration) {
- parentGuid =
- yield MigrationUtils.createImportedBookmarksFolder("360se", parentGuid);
- }
- idToGuid.set("fallback", parentGuid);
- }
-
- try {
- if (is_folder == 1) {
- let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- parentGuid,
- type: PlacesUtils.bookmarks.TYPE_FOLDER,
- title
- })).guid;
-
- idToGuid.set(id, newFolderGuid);
- } else {
- yield MigrationUtils.insertBookmarkWrapper({
- parentGuid,
- url,
- title
- });
- }
- } catch (ex) {
- Cu.reportError(ex);
- }
- }
- } finally {
- yield connection.close();
- }
- }.bind(this)).then(() => aCallback(true),
- e => { Cu.reportError(e); aCallback(false) });
- }
-};
-
-function Qihoo360seProfileMigrator() {
- let paths = [
- // for v6 and above
- {
- users: ["360se6", "apps", "data", "users"],
- defaultUser: "default"
- },
- // for earlier versions
- {
- users: ["360se"],
- defaultUser: "data"
- }
- ];
- this._usersDir = null;
- this._defaultUserPath = null;
- for (let path of paths) {
- let usersDir = FileUtils.getDir("AppData", path.users, false);
- if (usersDir.exists()) {
- this._usersDir = usersDir;
- this._defaultUserPath = path.defaultUser;
- break;
- }
- }
-}
-
-Qihoo360seProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-Object.defineProperty(Qihoo360seProfileMigrator.prototype, "sourceProfiles", {
- get: function() {
- if ("__sourceProfiles" in this)
- return this.__sourceProfiles;
-
- if (!this._usersDir) {
- this.__sourceProfiles = [];
- return this.__sourceProfiles;
- }
-
- let profiles = [];
- let noLoggedInUser = true;
- try {
- let loginIni = this._usersDir.clone();
- loginIni.append("login.ini");
- if (!loginIni.exists()) {
- throw new Error("360 Secure Browser's 'login.ini' does not exist.");
- }
- if (!loginIni.isReadable()) {
- throw new Error("360 Secure Browser's 'login.ini' file could not be read.");
- }
-
- let loginIniInUtf8 = copyToTempUTF8File(loginIni, "gbk");
- let loginIniObj = parseINIStrings(loginIniInUtf8);
- try {
- loginIniInUtf8.remove(false);
- } catch (ex) {}
-
- let nowLoginEmail = loginIniObj.NowLogin && loginIniObj.NowLogin.email;
-
- /*
- * NowLogin section may:
- * 1. be missing or without email, before any user logs in.
- * 2. represents the current logged in user
- * 3. represents the most recent logged in user
- *
- * In the second case, user represented by NowLogin should be the first
- * profile; otherwise the default user should be selected by default.
- */
- if (nowLoginEmail) {
- if (loginIniObj.NowLogin.IsLogined === "1") {
- noLoggedInUser = false;
- }
-
- profiles.push({
- id: this._getIdFromConfig(loginIniObj.NowLogin),
- name: nowLoginEmail,
- });
- }
-
- for (let section in loginIniObj) {
- if (!loginIniObj[section].email ||
- (nowLoginEmail && loginIniObj[section].email == nowLoginEmail)) {
- continue;
- }
-
- profiles.push({
- id: this._getIdFromConfig(loginIniObj[section]),
- name: loginIniObj[section].email,
- });
- }
- } catch (e) {
- Cu.reportError("Error detecting 360 Secure Browser profiles: " + e);
- } finally {
- profiles[noLoggedInUser ? "unshift" : "push"]({
- id: this._defaultUserPath,
- name: "Default",
- });
- }
-
- this.__sourceProfiles = profiles.filter(profile => {
- let resources = this.getResources(profile);
- return resources && resources.length > 0;
- });
- return this.__sourceProfiles;
- }
-});
-
-Qihoo360seProfileMigrator.prototype._getIdFromConfig = function(aConfig) {
- return aConfig.UserMd5 || getHash(aConfig.email);
-};
-
-Qihoo360seProfileMigrator.prototype.getResources = function(aProfile) {
- let profileFolder = this._usersDir.clone();
- profileFolder.append(aProfile.id);
-
- if (!profileFolder.exists()) {
- return [];
- }
-
- let resources = [
- new Bookmarks(profileFolder)
- ];
- return resources.filter(r => r.exists);
-};
-
-Qihoo360seProfileMigrator.prototype.getLastUsedDate = function() {
- let bookmarksPaths = this.sourceProfiles.map(({id}) => {
- return OS.Path.join(this._usersDir.path, id, kBookmarksFileName);
- });
- if (!bookmarksPaths.length) {
- return Promise.resolve(new Date(0));
- }
- let datePromises = bookmarksPaths.map(path => {
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
- });
- return Promise.all(datePromises).then(dates => {
- return new Date(Math.max.apply(Math, dates));
- });
-};
-
-Qihoo360seProfileMigrator.prototype.classDescription = "360 Secure Browser Profile Migrator";
-Qihoo360seProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=360se";
-Qihoo360seProfileMigrator.prototype.classID = Components.ID("{d0037b95-296a-4a4e-94b2-c3d075d20ab1}");
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Qihoo360seProfileMigrator]);
diff --git a/browser/components/migration/AutoMigrate.jsm b/browser/components/migration/AutoMigrate.jsm
deleted file mode 100644
index b38747825..000000000
--- a/browser/components/migration/AutoMigrate.jsm
+++ /dev/null
@@ -1,670 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["AutoMigrate"];
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-
-const kAutoMigrateEnabledPref = "browser.migrate.automigrate.enabled";
-const kUndoUIEnabledPref = "browser.migrate.automigrate.ui.enabled";
-
-const kAutoMigrateBrowserPref = "browser.migrate.automigrate.browser";
-const kAutoMigrateImportedItemIds = "browser.migrate.automigrate.imported-items";
-
-const kAutoMigrateLastUndoPromptDateMsPref = "browser.migrate.automigrate.lastUndoPromptDateMs";
-const kAutoMigrateDaysToOfferUndoPref = "browser.migrate.automigrate.daysToOfferUndo";
-
-const kAutoMigrateUndoSurveyPref = "browser.migrate.automigrate.undo-survey";
-const kAutoMigrateUndoSurveyLocalePref = "browser.migrate.automigrate.undo-survey-locales";
-
-const kNotificationId = "automigration-undo";
-
-Cu.import("resource:///modules/MigrationUtils.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
- "resource://gre/modules/AsyncShutdown.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
- "resource://gre/modules/LoginHelper.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
- "resource://gre/modules/NewTabUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
- "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
- "resource://gre/modules/TelemetryStopwatch.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
- const kBrandBundle = "chrome://branding/locale/brand.properties";
- return Services.strings.createBundle(kBrandBundle);
-});
-
-XPCOMUtils.defineLazyGetter(this, "gHardcodedStringBundle", function() {
- const kBundleURI = "chrome://browser/content/migration/extra-migration-strings.properties";
- return Services.strings.createBundle(kBundleURI);
-});
-
-Cu.importGlobalProperties(["URL"]);
-
-/* globals kUndoStateFullPath */
-XPCOMUtils.defineLazyGetter(this, "kUndoStateFullPath", function() {
- return OS.Path.join(OS.Constants.Path.profileDir, "initialMigrationMetadata.jsonlz4");
-});
-
-const AutoMigrate = {
- get resourceTypesToUse() {
- let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
- return BOOKMARKS | HISTORY | PASSWORDS;
- },
-
- _checkIfEnabled() {
- let pref = Preferences.get(kAutoMigrateEnabledPref, false);
- // User-set values should take precedence:
- if (Services.prefs.prefHasUserValue(kAutoMigrateEnabledPref)) {
- return pref;
- }
- // If we're using the default value, make sure the distribution.ini
- // value is taken into account even early on startup.
- try {
- let distributionFile = Services.dirsvc.get("XREAppDist", Ci.nsIFile);
- distributionFile.append("distribution.ini");
- let parser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
- getService(Ci.nsIINIParserFactory).
- createINIParser(distributionFile);
- return JSON.parse(parser.getString("Preferences", kAutoMigrateEnabledPref));
- } catch (ex) { /* ignore exceptions (file doesn't exist, invalid value, etc.) */ }
-
- return pref;
- },
-
- init() {
- this.enabled = this._checkIfEnabled();
- },
-
- /**
- * Automatically pick a migrator and resources to migrate,
- * then migrate those and start up.
- *
- * @throws if automatically deciding on migrators/data
- * failed for some reason.
- */
- migrate(profileStartup, migratorKey, profileToMigrate) {
- let histogram = Services.telemetry.getHistogramById(
- "FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_PROCESS_SUCCESS");
- histogram.add(0);
- let {migrator, pickedKey} = this.pickMigrator(migratorKey);
- histogram.add(5);
-
- profileToMigrate = this.pickProfile(migrator, profileToMigrate);
- histogram.add(10);
-
- let resourceTypes = migrator.getMigrateData(profileToMigrate, profileStartup);
- if (!(resourceTypes & this.resourceTypesToUse)) {
- throw new Error("No usable resources were found for the selected browser!");
- }
- histogram.add(15);
-
- let sawErrors = false;
- let migrationObserver = (subject, topic) => {
- if (topic == "Migration:ItemError") {
- sawErrors = true;
- } else if (topic == "Migration:Ended") {
- histogram.add(25);
- if (sawErrors) {
- histogram.add(26);
- }
- Services.obs.removeObserver(migrationObserver, "Migration:Ended");
- Services.obs.removeObserver(migrationObserver, "Migration:ItemError");
- Services.prefs.setCharPref(kAutoMigrateBrowserPref, pickedKey);
- // Save the undo history and block shutdown on that save completing.
- AsyncShutdown.profileBeforeChange.addBlocker(
- "AutoMigrate Undo saving", this.saveUndoState(), () => {
- return {state: this._saveUndoStateTrackerForShutdown};
- });
- }
- };
-
- MigrationUtils.initializeUndoData();
- Services.obs.addObserver(migrationObserver, "Migration:Ended", false);
- Services.obs.addObserver(migrationObserver, "Migration:ItemError", false);
- migrator.migrate(this.resourceTypesToUse, profileStartup, profileToMigrate);
- histogram.add(20);
- },
-
- /**
- * Pick and return a migrator to use for automatically migrating.
- *
- * @param {String} migratorKey optional, a migrator key to prefer/pick.
- * @returns {Object} an object with the migrator to use for migrating, as
- * well as the key we eventually ended up using to obtain it.
- */
- pickMigrator(migratorKey) {
- if (!migratorKey) {
- let defaultKey = MigrationUtils.getMigratorKeyForDefaultBrowser();
- if (!defaultKey) {
- throw new Error("Could not determine default browser key to migrate from");
- }
- migratorKey = defaultKey;
- }
- if (migratorKey == "firefox") {
- throw new Error("Can't automatically migrate from Firefox.");
- }
-
- let migrator = MigrationUtils.getMigrator(migratorKey);
- if (!migrator) {
- throw new Error("Migrator specified or a default was found, but the migrator object is not available (or has no data).");
- }
- return {migrator, pickedKey: migratorKey};
- },
-
- /**
- * Pick a source profile (from the original browser) to use.
- *
- * @param {Migrator} migrator the migrator object to use
- * @param {String} suggestedId the id of the profile to migrate, if pre-specified, or null
- * @returns the profile to migrate, or null if migrating
- * from the default profile.
- */
- pickProfile(migrator, suggestedId) {
- let profiles = migrator.sourceProfiles;
- if (profiles && !profiles.length) {
- throw new Error("No profile data found to migrate.");
- }
- if (suggestedId) {
- if (!profiles) {
- throw new Error("Profile specified but only a default profile found.");
- }
- let suggestedProfile = profiles.find(profile => profile.id == suggestedId);
- if (!suggestedProfile) {
- throw new Error("Profile specified was not found.");
- }
- return suggestedProfile;
- }
- if (profiles && profiles.length > 1) {
- throw new Error("Don't know how to pick a profile when more than 1 profile is present.");
- }
- return profiles ? profiles[0] : null;
- },
-
- _pendingUndoTasks: false,
- canUndo: Task.async(function* () {
- if (this._savingPromise) {
- yield this._savingPromise;
- }
- if (this._pendingUndoTasks) {
- return false;
- }
- let fileExists = false;
- try {
- fileExists = yield OS.File.exists(kUndoStateFullPath);
- } catch (ex) {
- Cu.reportError(ex);
- }
- return fileExists;
- }),
-
- undo: Task.async(function* () {
- let browserId = Preferences.get(kAutoMigrateBrowserPref, "unknown");
- TelemetryStopwatch.startKeyed("FX_STARTUP_MIGRATION_UNDO_TOTAL_MS", browserId);
- let histogram = Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_UNDO");
- histogram.add(0);
- if (!(yield this.canUndo())) {
- histogram.add(5);
- throw new Error("Can't undo!");
- }
-
- this._pendingUndoTasks = true;
- this._removeNotificationBars();
- histogram.add(10);
-
- let readPromise = OS.File.read(kUndoStateFullPath, {
- encoding: "utf-8",
- compression: "lz4",
- });
- let stateData = this._dejsonifyUndoState(yield readPromise);
- histogram.add(12);
-
- this._errorMap = {bookmarks: 0, visits: 0, logins: 0};
- let reportErrorTelemetry = (type) => {
- let histogramId = `FX_STARTUP_MIGRATION_UNDO_${type.toUpperCase()}_ERRORCOUNT`;
- Services.telemetry.getKeyedHistogramById(histogramId).add(browserId, this._errorMap[type]);
- };
-
- let startTelemetryStopwatch = resourceType => {
- let histogramId = `FX_STARTUP_MIGRATION_UNDO_${resourceType.toUpperCase()}_MS`;
- TelemetryStopwatch.startKeyed(histogramId, browserId);
- };
- let stopTelemetryStopwatch = resourceType => {
- let histogramId = `FX_STARTUP_MIGRATION_UNDO_${resourceType.toUpperCase()}_MS`;
- TelemetryStopwatch.finishKeyed(histogramId, browserId);
- };
- startTelemetryStopwatch("bookmarks");
- yield this._removeUnchangedBookmarks(stateData.get("bookmarks")).catch(ex => {
- Cu.reportError("Uncaught exception when removing unchanged bookmarks!");
- Cu.reportError(ex);
- });
- stopTelemetryStopwatch("bookmarks");
- reportErrorTelemetry("bookmarks");
- histogram.add(15);
-
- startTelemetryStopwatch("visits");
- yield this._removeSomeVisits(stateData.get("visits")).catch(ex => {
- Cu.reportError("Uncaught exception when removing history visits!");
- Cu.reportError(ex);
- });
- stopTelemetryStopwatch("visits");
- reportErrorTelemetry("visits");
- histogram.add(20);
-
- startTelemetryStopwatch("logins");
- yield this._removeUnchangedLogins(stateData.get("logins")).catch(ex => {
- Cu.reportError("Uncaught exception when removing unchanged logins!");
- Cu.reportError(ex);
- });
- stopTelemetryStopwatch("logins");
- reportErrorTelemetry("logins");
- histogram.add(25);
-
- // This is async, but no need to wait for it.
- NewTabUtils.links.populateCache(() => {
- NewTabUtils.allPages.update();
- }, true);
-
- this._purgeUndoState(this.UNDO_REMOVED_REASON_UNDO_USED);
- histogram.add(30);
- TelemetryStopwatch.finishKeyed("FX_STARTUP_MIGRATION_UNDO_TOTAL_MS", browserId);
- }),
-
- _removeNotificationBars() {
- let browserWindows = Services.wm.getEnumerator("navigator:browser");
- while (browserWindows.hasMoreElements()) {
- let win = browserWindows.getNext();
- if (!win.closed) {
- for (let browser of win.gBrowser.browsers) {
- let nb = win.gBrowser.getNotificationBox(browser);
- let notification = nb.getNotificationWithValue(kNotificationId);
- if (notification) {
- nb.removeNotification(notification);
- }
- }
- }
- }
- },
-
- _purgeUndoState(reason) {
- // We don't wait for the off-main-thread removal to complete. OS.File will
- // ensure it happens before shutdown.
- OS.File.remove(kUndoStateFullPath, {ignoreAbsent: true}).then(() => {
- this._pendingUndoTasks = false;
- });
-
- let migrationBrowser = Preferences.get(kAutoMigrateBrowserPref, "unknown");
- Services.prefs.clearUserPref(kAutoMigrateBrowserPref);
-
- let histogram =
- Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_UNDO_REASON");
- histogram.add(migrationBrowser, reason);
- },
-
- getBrowserUsedForMigration() {
- let browserId = Services.prefs.getCharPref(kAutoMigrateBrowserPref);
- if (browserId) {
- return MigrationUtils.getBrowserName(browserId);
- }
- return null;
- },
-
- /**
- * Show the user a notification bar indicating we automatically imported
- * their data and offering them the possibility of removing it.
- * @param target (xul:browser)
- * The browser in which we should show the notification.
- */
- maybeShowUndoNotification: Task.async(function* (target) {
- if (!(yield this.canUndo())) {
- return;
- }
-
- // The tab might have navigated since we requested the undo state:
- let canUndoFromThisPage = ["about:home", "about:newtab"].includes(target.currentURI.spec);
- if (!canUndoFromThisPage ||
- !Preferences.get(kUndoUIEnabledPref, false)) {
- return;
- }
-
- let win = target.ownerGlobal;
- let notificationBox = win.gBrowser.getNotificationBox(target);
- if (!notificationBox || notificationBox.getNotificationWithValue(kNotificationId)) {
- return;
- }
-
- // At this stage we're committed to show the prompt - unless we shouldn't,
- // in which case we remove the undo prefs (which will cause canUndo() to
- // return false from now on.):
- if (!this.shouldStillShowUndoPrompt()) {
- this._purgeUndoState(this.UNDO_REMOVED_REASON_OFFER_EXPIRED);
- this._removeNotificationBars();
- return;
- }
-
- let browserName = this.getBrowserUsedForMigration();
- if (!browserName) {
- browserName = gHardcodedStringBundle.GetStringFromName("automigration.undo.unknownbrowser");
- }
- const kMessageId = "automigration.undo.message." +
- Preferences.get(kAutoMigrateImportedItemIds, "all");
- const kBrandShortName = gBrandBundle.GetStringFromName("brandShortName");
- let message = gHardcodedStringBundle.formatStringFromName(kMessageId,
- [browserName, kBrandShortName], 2);
-
- let buttons = [
- {
- label: gHardcodedStringBundle.GetStringFromName("automigration.undo.keep2.label"),
- accessKey: gHardcodedStringBundle.GetStringFromName("automigration.undo.keep2.accesskey"),
- callback: () => {
- this._purgeUndoState(this.UNDO_REMOVED_REASON_OFFER_REJECTED);
- this._removeNotificationBars();
- },
- },
- {
- label: gHardcodedStringBundle.GetStringFromName("automigration.undo.dontkeep2.label"),
- accessKey: gHardcodedStringBundle.GetStringFromName("automigration.undo.dontkeep2.accesskey"),
- callback: () => {
- this._maybeOpenUndoSurveyTab(win);
- this.undo();
- },
- },
- ];
- notificationBox.appendNotification(
- message, kNotificationId, null, notificationBox.PRIORITY_INFO_HIGH, buttons
- );
- let remainingDays = Preferences.get(kAutoMigrateDaysToOfferUndoPref, 0);
- Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_UNDO_OFFERED").add(4 - remainingDays);
- }),
-
- shouldStillShowUndoPrompt() {
- let today = new Date();
- // Round down to midnight:
- today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
- // We store the unix timestamp corresponding to midnight on the last day
- // on which we prompted. Fetch that and compare it to today's date.
- // (NB: stored as a string because int prefs are too small for unix
- // timestamps.)
- let previousPromptDateMsStr = Preferences.get(kAutoMigrateLastUndoPromptDateMsPref, "0");
- let previousPromptDate = new Date(parseInt(previousPromptDateMsStr, 10));
- if (previousPromptDate < today) {
- let remainingDays = Preferences.get(kAutoMigrateDaysToOfferUndoPref, 4) - 1;
- Preferences.set(kAutoMigrateDaysToOfferUndoPref, remainingDays);
- Preferences.set(kAutoMigrateLastUndoPromptDateMsPref, today.valueOf().toString());
- if (remainingDays <= 0) {
- return false;
- }
- }
- return true;
- },
-
- UNDO_REMOVED_REASON_UNDO_USED: 0,
- UNDO_REMOVED_REASON_SYNC_SIGNIN: 1,
- UNDO_REMOVED_REASON_PASSWORD_CHANGE: 2,
- UNDO_REMOVED_REASON_BOOKMARK_CHANGE: 3,
- UNDO_REMOVED_REASON_OFFER_EXPIRED: 4,
- UNDO_REMOVED_REASON_OFFER_REJECTED: 5,
-
- _jsonifyUndoState(state) {
- if (!state) {
- return "null";
- }
- // Deal with date serialization.
- let bookmarks = state.get("bookmarks");
- for (let bm of bookmarks) {
- bm.lastModified = bm.lastModified.getTime();
- }
- let serializableState = {
- bookmarks,
- logins: state.get("logins"),
- visits: state.get("visits"),
- };
- return JSON.stringify(serializableState);
- },
-
- _dejsonifyUndoState(state) {
- state = JSON.parse(state);
- if (!state) {
- return new Map();
- }
- for (let bm of state.bookmarks) {
- bm.lastModified = new Date(bm.lastModified);
- }
- return new Map([
- ["bookmarks", state.bookmarks],
- ["logins", state.logins],
- ["visits", state.visits],
- ]);
- },
-
- /**
- * Store the items we've saved into a pref. We use this to be able to show
- * a detailed message to the user indicating what we've imported.
- * @param state (Map)
- * The 'undo' state for the import, which contains info about
- * how many items of each kind we've (tried to) import.
- */
- _setImportedItemPrefFromState(state) {
- let itemsWithData = [];
- if (state) {
- for (let itemType of state.keys()) {
- if (state.get(itemType).length) {
- itemsWithData.push(itemType);
- }
- }
- }
- if (itemsWithData.length == 3) {
- itemsWithData = "all";
- } else {
- itemsWithData = itemsWithData.sort().join(".");
- }
- if (itemsWithData) {
- Preferences.set(kAutoMigrateImportedItemIds, itemsWithData);
- }
- },
-
- /**
- * Used for the shutdown blocker's information field.
- */
- _saveUndoStateTrackerForShutdown: "not running",
- /**
- * Store the information required for using 'undo' of the automatic
- * migration in the user's profile.
- */
- saveUndoState: Task.async(function* () {
- let resolveSavingPromise;
- this._saveUndoStateTrackerForShutdown = "processing undo history";
- this._savingPromise = new Promise(resolve => { resolveSavingPromise = resolve });
- let state = yield MigrationUtils.stopAndRetrieveUndoData();
-
- if (!state || ![...state.values()].some(ary => ary.length > 0)) {
- // If we didn't import anything, abort now.
- resolveSavingPromise();
- return Promise.resolve();
- }
-
- this._saveUndoStateTrackerForShutdown = "saving imported item list";
- this._setImportedItemPrefFromState(state);
-
- this._saveUndoStateTrackerForShutdown = "writing undo history";
- this._undoSavePromise = OS.File.writeAtomic(
- kUndoStateFullPath, this._jsonifyUndoState(state), {
- encoding: "utf-8",
- compression: "lz4",
- tmpPath: kUndoStateFullPath + ".tmp",
- });
- this._undoSavePromise.then(
- rv => {
- resolveSavingPromise(rv);
- delete this._savingPromise;
- },
- e => {
- Cu.reportError("Could not write undo state for automatic migration.");
- throw e;
- });
- return this._undoSavePromise;
- }),
-
- _removeUnchangedBookmarks: Task.async(function* (bookmarks) {
- if (!bookmarks.length) {
- return;
- }
-
- let guidToLMMap = new Map(bookmarks.map(b => [b.guid, b.lastModified]));
- let bookmarksFromDB = [];
- let bmPromises = Array.from(guidToLMMap.keys()).map(guid => {
- // Ignore bookmarks where the promise doesn't resolve (ie that are missing)
- // Also check that the bookmark fetch returns isn't null before adding it.
- try {
- return PlacesUtils.bookmarks.fetch(guid).then(bm => bm && bookmarksFromDB.push(bm), () => {});
- } catch (ex) {
- // Ignore immediate exceptions, too.
- }
- return Promise.resolve();
- });
- // We can't use the result of Promise.all because that would include nulls
- // for bookmarks that no longer exist (which we're catching above).
- yield Promise.all(bmPromises);
- let unchangedBookmarks = bookmarksFromDB.filter(bm => {
- return bm.lastModified.getTime() == guidToLMMap.get(bm.guid).getTime();
- });
-
- // We need to remove items without children first, followed by their
- // parents, etc. In order to do this, find out how many ancestors each item
- // has that also appear in our list of things to remove, and sort the items
- // by those numbers. This ensures that children are always removed before
- // their parents.
- function determineAncestorCount(bm) {
- if (bm._ancestorCount) {
- return bm._ancestorCount;
- }
- let myCount = 0;
- let parentBM = unchangedBookmarks.find(item => item.guid == bm.parentGuid);
- if (parentBM) {
- myCount = determineAncestorCount(parentBM) + 1;
- }
- bm._ancestorCount = myCount;
- return myCount;
- }
- unchangedBookmarks.forEach(determineAncestorCount);
- unchangedBookmarks.sort((a, b) => b._ancestorCount - a._ancestorCount);
- for (let {guid} of unchangedBookmarks) {
- // Can't just use a .catch() because Bookmarks.remove() can throw (rather
- // than returning rejected promises).
- try {
- yield PlacesUtils.bookmarks.remove(guid, {preventRemovalOfNonEmptyFolders: true});
- } catch (err) {
- if (err && err.message != "Cannot remove a non-empty folder.") {
- this._errorMap.bookmarks++;
- Cu.reportError(err);
- }
- }
- }
- }),
-
- _removeUnchangedLogins: Task.async(function* (logins) {
- for (let login of logins) {
- let foundLogins = LoginHelper.searchLoginsWithObject({guid: login.guid});
- if (foundLogins.length) {
- let foundLogin = foundLogins[0];
- foundLogin.QueryInterface(Ci.nsILoginMetaInfo);
- if (foundLogin.timePasswordChanged == login.timePasswordChanged) {
- try {
- Services.logins.removeLogin(foundLogin);
- } catch (ex) {
- Cu.reportError("Failed to remove a login for " + foundLogins.hostname);
- Cu.reportError(ex);
- this._errorMap.logins++;
- }
- }
- }
- }
- }),
-
- _removeSomeVisits: Task.async(function* (visits) {
- for (let urlVisits of visits) {
- let urlObj;
- try {
- urlObj = new URL(urlVisits.url);
- } catch (ex) {
- continue;
- }
- let visitData = {
- url: urlObj,
- beginDate: PlacesUtils.toDate(urlVisits.first),
- endDate: PlacesUtils.toDate(urlVisits.last),
- limit: urlVisits.visitCount,
- };
- try {
- yield PlacesUtils.history.removeVisitsByFilter(visitData);
- } catch (ex) {
- this._errorMap.visits++;
- try {
- visitData.url = visitData.url.href;
- } catch (ignoredEx) {}
- Cu.reportError("Failed to remove a visit: " + JSON.stringify(visitData));
- Cu.reportError(ex);
- }
- }
- }),
-
- /**
- * Maybe open a new tab with a survey. The tab will only be opened if all of
- * the following are true:
- * - the 'browser.migrate.automigrate.undo-survey' pref is not empty.
- * It should contain the URL of the survey to open.
- * - the 'browser.migrate.automigrate.undo-survey-locales' pref, a
- * comma-separated list of language codes, contains the language code
- * that is currently in use for the 'global' chrome pacakge (ie the
- * locale in which the user is currently using Firefox).
- * The URL will be passed through nsIURLFormatter to allow including
- * build ids etc. The special additional formatting variable
- * "%IMPORTEDBROWSER" is also replaced with the name of the browser
- * from which we imported data.
- *
- * @param {Window} chromeWindow A reference to the window in which to open a link.
- */
- _maybeOpenUndoSurveyTab(chromeWindow) {
- let canDoSurveyInLocale = false;
- try {
- let surveyLocales = Preferences.get(kAutoMigrateUndoSurveyLocalePref, "");
- surveyLocales = surveyLocales.split(",").map(str => str.trim());
- // Strip out any empty elements, so an empty pref doesn't
- // lead to a an array with 1 empty string in it.
- surveyLocales = new Set(surveyLocales.filter(str => !!str));
- let chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"]
- .getService(Ci.nsIXULChromeRegistry);
- canDoSurveyInLocale =
- surveyLocales.has(chromeRegistry.getSelectedLocale("global"));
- } catch (ex) {
- /* ignore exceptions and just don't do the survey. */
- }
-
- let migrationBrowser = this.getBrowserUsedForMigration();
- let rawURL = Preferences.get(kAutoMigrateUndoSurveyPref, "");
- if (!canDoSurveyInLocale || !migrationBrowser || !rawURL) {
- return;
- }
-
- let url = Services.urlFormatter.formatURL(rawURL);
- url = url.replace("%IMPORTEDBROWSER%", encodeURIComponent(migrationBrowser));
- chromeWindow.openUILinkIn(url, "tab");
- },
-
- QueryInterface: XPCOMUtils.generateQI(
- [Ci.nsIObserver, Ci.nsINavBookmarkObserver, Ci.nsISupportsWeakReference]
- ),
-};
-
-AutoMigrate.init();
diff --git a/browser/components/migration/BrowserProfileMigrators.manifest b/browser/components/migration/BrowserProfileMigrators.manifest
deleted file mode 100644
index e16fba13a..000000000
--- a/browser/components/migration/BrowserProfileMigrators.manifest
+++ /dev/null
@@ -1,33 +0,0 @@
-component {6F8BB968-C14F-4D6F-9733-6C6737B35DCE} ProfileMigrator.js
-contract @mozilla.org/toolkit/profile-migrator;1 {6F8BB968-C14F-4D6F-9733-6C6737B35DCE}
-
-#if defined(XP_WIN) || defined(XP_MACOSX)
-component {4bf85aa5-4e21-46ca-825f-f9c51a5e8c76} ChromeProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=canary {4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}
-#endif
-component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
-component {8cece922-9720-42de-b7db-7cef88cb07ca} ChromeProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=chromium {8cece922-9720-42de-b7db-7cef88cb07ca}
-
-component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
-
-#ifdef HAS_IE_MIGRATOR
-component {3d2532e3-4932-4774-b7ba-968f5899d3a4} IEProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=ie {3d2532e3-4932-4774-b7ba-968f5899d3a4}
-#endif
-
-#ifdef HAS_EDGE_MIGRATOR
-component {62e8834b-2d17-49f5-96ff-56344903a2ae} EdgeProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=edge {62e8834b-2d17-49f5-96ff-56344903a2ae}
-#endif
-
-#ifdef HAS_SAFARI_MIGRATOR
-component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1}
-#endif
-#ifdef HAS_360SE_MIGRATOR
-component {d0037b95-296a-4a4e-94b2-c3d075d20ab1} 360seProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=360se {d0037b95-296a-4a4e-94b2-c3d075d20ab1}
-#endif
diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js
deleted file mode 100644
index ec0c8d444..000000000
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ /dev/null
@@ -1,557 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * vim: sw=2 ts=2 sts=2 et */
-/* 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/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-
-const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
-
-const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
-const S100NS_PER_MS = 10;
-
-const AUTH_TYPE = {
- SCHEME_HTML: 0,
- SCHEME_BASIC: 1,
- SCHEME_DIGEST: 2
-};
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
- "resource://gre/modules/OSCrypto.jsm");
-/**
- * Get an nsIFile instance representing the expected location of user data
- * for this copy of Chrome/Chromium/Canary on different OSes.
- * @param subfoldersWin {Array} an array of subfolders to use for Windows
- * @param subfoldersOSX {Array} an array of subfolders to use for OS X
- * @param subfoldersUnix {Array} an array of subfolders to use for *nix systems
- * @returns {nsIFile} the place we expect data to live. Might not actually exist!
- */
-function getDataFolder(subfoldersWin, subfoldersOSX, subfoldersUnix) {
- let dirServiceID, subfolders;
- if (AppConstants.platform == "win") {
- dirServiceID = "LocalAppData";
- subfolders = subfoldersWin.concat(["User Data"]);
- } else if (AppConstants.platform == "macosx") {
- dirServiceID = "ULibDir";
- subfolders = ["Application Support"].concat(subfoldersOSX);
- } else {
- dirServiceID = "Home";
- subfolders = [".config"].concat(subfoldersUnix);
- }
- return FileUtils.getDir(dirServiceID, subfolders, false);
-}
-
-/**
- * Convert Chrome time format to Date object
- *
- * @param aTime
- * Chrome time
- * @return converted Date object
- * @note Google Chrome uses FILETIME / 10 as time.
- * FILETIME is based on same structure of Windows.
- */
-function chromeTimeToDate(aTime)
-{
- return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970) / 10000);
-}
-
-/**
- * Convert Date object to Chrome time format
- *
- * @param aDate
- * Date object or integer equivalent
- * @return Chrome time
- * @note For details on Chrome time, see chromeTimeToDate.
- */
-function dateToChromeTime(aDate) {
- return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS;
-}
-
-/**
- * Insert bookmark items into specific folder.
- *
- * @param parentGuid
- * GUID of the folder where items will be inserted
- * @param items
- * bookmark items to be inserted
- * @param errorAccumulator
- * function that gets called with any errors thrown so we don't drop them on the floor.
- */
-function* insertBookmarkItems(parentGuid, items, errorAccumulator) {
- for (let item of items) {
- try {
- if (item.type == "url") {
- if (item.url.trim().startsWith("chrome:")) {
- // Skip invalid chrome URIs. Creating an actual URI always reports
- // messages to the console, so we avoid doing that.
- continue;
- }
- yield MigrationUtils.insertBookmarkWrapper({
- parentGuid, url: item.url, title: item.name
- });
- } else if (item.type == "folder") {
- let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name
- })).guid;
-
- yield insertBookmarkItems(newFolderGuid, item.children, errorAccumulator);
- }
- } catch (e) {
- Cu.reportError(e);
- errorAccumulator(e);
- }
- }
-}
-
-function ChromeProfileMigrator() {
- let chromeUserDataFolder =
- getDataFolder(["Google", "Chrome"], ["Google", "Chrome"], ["google-chrome"]);
- this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
- chromeUserDataFolder : null;
-}
-
-ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-ChromeProfileMigrator.prototype.getResources =
- function Chrome_getResources(aProfile) {
- if (this._chromeUserDataFolder) {
- let profileFolder = this._chromeUserDataFolder.clone();
- profileFolder.append(aProfile.id);
- if (profileFolder.exists()) {
- let possibleResources = [
- GetBookmarksResource(profileFolder),
- GetHistoryResource(profileFolder),
- GetCookiesResource(profileFolder),
- ];
- if (AppConstants.platform == "win") {
- possibleResources.push(GetWindowsPasswordsResource(profileFolder));
- }
- return possibleResources.filter(r => r != null);
- }
- }
- return [];
- };
-
-ChromeProfileMigrator.prototype.getLastUsedDate =
- function Chrome_getLastUsedDate() {
- let datePromises = this.sourceProfiles.map(profile => {
- let basePath = OS.Path.join(this._chromeUserDataFolder.path, profile.id);
- let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(leafName => {
- let path = OS.Path.join(basePath, leafName);
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
- });
- return Promise.all(fileDatePromises).then(dates => {
- return Math.max.apply(Math, dates);
- });
- });
- return Promise.all(datePromises).then(dates => {
- dates.push(0);
- return new Date(Math.max.apply(Math, dates));
- });
- };
-
-Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
- get: function Chrome_sourceProfiles() {
- if ("__sourceProfiles" in this)
- return this.__sourceProfiles;
-
- if (!this._chromeUserDataFolder)
- return [];
-
- let profiles = [];
- try {
- // Local State is a JSON file that contains profile info.
- let localState = this._chromeUserDataFolder.clone();
- localState.append("Local State");
- if (!localState.exists())
- throw new Error("Chrome's 'Local State' file does not exist.");
- if (!localState.isReadable())
- throw new Error("Chrome's 'Local State' file could not be read.");
-
- let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
- fstream.init(localState, -1, 0, 0);
- let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
- { charset: "UTF-8" });
- let info_cache = JSON.parse(inputStream).profile.info_cache;
- for (let profileFolderName in info_cache) {
- let profileFolder = this._chromeUserDataFolder.clone();
- profileFolder.append(profileFolderName);
- profiles.push({
- id: profileFolderName,
- name: info_cache[profileFolderName].name || profileFolderName,
- });
- }
- } catch (e) {
- Cu.reportError("Error detecting Chrome profiles: " + e);
- // If we weren't able to detect any profiles above, fallback to the Default profile.
- let defaultProfileFolder = this._chromeUserDataFolder.clone();
- defaultProfileFolder.append("Default");
- if (defaultProfileFolder.exists()) {
- profiles = [{
- id: "Default",
- name: "Default",
- }];
- }
- }
-
- // Only list profiles from which any data can be imported
- this.__sourceProfiles = profiles.filter(function(profile) {
- let resources = this.getResources(profile);
- return resources && resources.length > 0;
- }, this);
- return this.__sourceProfiles;
- }
-});
-
-Object.defineProperty(ChromeProfileMigrator.prototype, "sourceHomePageURL", {
- get: function Chrome_sourceHomePageURL() {
- let prefsFile = this._chromeUserDataFolder.clone();
- prefsFile.append("Preferences");
- if (prefsFile.exists()) {
- // XXX reading and parsing JSON is synchronous.
- let fstream = Cc[FILE_INPUT_STREAM_CID].
- createInstance(Ci.nsIFileInputStream);
- fstream.init(prefsFile, -1, 0, 0);
- try {
- return JSON.parse(
- NetUtil.readInputStreamToString(fstream, fstream.available(),
- { charset: "UTF-8" })
- ).homepage;
- }
- catch (e) {
- Cu.reportError("Error parsing Chrome's preferences file: " + e);
- }
- }
- return "";
- }
-});
-
-Object.defineProperty(ChromeProfileMigrator.prototype, "sourceLocked", {
- get: function Chrome_sourceLocked() {
- // There is an exclusive lock on some SQLite databases. Assume they are locked for now.
- return true;
- },
-});
-
-function GetBookmarksResource(aProfileFolder) {
- let bookmarksFile = aProfileFolder.clone();
- bookmarksFile.append("Bookmarks");
- if (!bookmarksFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- migrate: function(aCallback) {
- return Task.spawn(function* () {
- let gotErrors = false;
- let errorGatherer = function() { gotErrors = true };
- let jsonStream = yield new Promise((resolve, reject) => {
- let options = {
- uri: NetUtil.newURI(bookmarksFile),
- loadUsingSystemPrincipal: true
- };
- NetUtil.asyncFetch(options, (inputStream, resultCode) => {
- if (Components.isSuccessCode(resultCode)) {
- resolve(inputStream);
- } else {
- reject(new Error("Could not read Bookmarks file"));
- }
- });
- });
-
- // Parse Chrome bookmark file that is JSON format
- let bookmarkJSON = NetUtil.readInputStreamToString(
- jsonStream, jsonStream.available(), { charset : "UTF-8" });
- let roots = JSON.parse(bookmarkJSON).roots;
-
- // Importing bookmark bar items
- if (roots.bookmark_bar.children &&
- roots.bookmark_bar.children.length > 0) {
- // Toolbar
- let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
- if (!MigrationUtils.isStartupMigration) {
- parentGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
- }
- yield insertBookmarkItems(parentGuid, roots.bookmark_bar.children, errorGatherer);
- }
-
- // Importing bookmark menu items
- if (roots.other.children &&
- roots.other.children.length > 0) {
- // Bookmark menu
- let parentGuid = PlacesUtils.bookmarks.menuGuid;
- if (!MigrationUtils.isStartupMigration) {
- parentGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
- }
- yield insertBookmarkItems(parentGuid, roots.other.children, errorGatherer);
- }
- if (gotErrors) {
- throw new Error("The migration included errors.");
- }
- }.bind(this)).then(() => aCallback(true),
- () => aCallback(false));
- }
- };
-}
-
-function GetHistoryResource(aProfileFolder) {
- let historyFile = aProfileFolder.clone();
- historyFile.append("History");
- if (!historyFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.HISTORY,
-
- migrate(aCallback) {
- Task.spawn(function* () {
- const MAX_AGE_IN_DAYS = Services.prefs.getIntPref("browser.migrate.chrome.history.maxAgeInDays");
- const LIMIT = Services.prefs.getIntPref("browser.migrate.chrome.history.limit");
-
- let query = "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0";
- if (MAX_AGE_IN_DAYS) {
- let maxAge = dateToChromeTime(Date.now() - MAX_AGE_IN_DAYS * 24 * 60 * 60 * 1000);
- query += " AND last_visit_time > " + maxAge;
- }
- if (LIMIT) {
- query += " ORDER BY last_visit_time DESC LIMIT " + LIMIT;
- }
-
- let rows =
- yield MigrationUtils.getRowsFromDBWithoutLocks(historyFile.path, "Chrome history", query);
- let places = [];
- for (let row of rows) {
- try {
- // if having typed_count, we changes transition type to typed.
- let transType = PlacesUtils.history.TRANSITION_LINK;
- if (row.getResultByName("typed_count") > 0)
- transType = PlacesUtils.history.TRANSITION_TYPED;
-
- places.push({
- uri: NetUtil.newURI(row.getResultByName("url")),
- title: row.getResultByName("title"),
- visits: [{
- transitionType: transType,
- visitDate: chromeTimeToDate(
- row.getResultByName(
- "last_visit_time")) * 1000,
- }],
- });
- } catch (e) {
- Cu.reportError(e);
- }
- }
-
- if (places.length > 0) {
- yield new Promise((resolve, reject) => {
- MigrationUtils.insertVisitsWrapper(places, {
- _success: false,
- handleResult: function() {
- // Importing any entry is considered a successful import.
- this._success = true;
- },
- handleError: function() {},
- handleCompletion: function() {
- if (this._success) {
- resolve();
- } else {
- reject(new Error("Couldn't add visits"));
- }
- }
- });
- });
- }
- }).then(() => { aCallback(true) },
- ex => {
- Cu.reportError(ex);
- aCallback(false);
- });
- }
- };
-}
-
-function GetCookiesResource(aProfileFolder) {
- let cookiesFile = aProfileFolder.clone();
- cookiesFile.append("Cookies");
- if (!cookiesFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.COOKIES,
-
- migrate: Task.async(function* (aCallback) {
- // We don't support decrypting cookies yet so only import plaintext ones.
- let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(cookiesFile.path, "Chrome cookies",
- `SELECT host_key, name, value, path, expires_utc, secure, httponly, encrypted_value
- FROM cookies
- WHERE length(encrypted_value) = 0`).catch(ex => {
- Cu.reportError(ex);
- aCallback(false);
- });
- // If the promise was rejected we will have already called aCallback,
- // so we can just return here.
- if (!rows) {
- return;
- }
-
- for (let row of rows) {
- let host_key = row.getResultByName("host_key");
- if (host_key.match(/^\./)) {
- // 1st character of host_key may be ".", so we have to remove it
- host_key = host_key.substr(1);
- }
-
- try {
- let expiresUtc =
- chromeTimeToDate(row.getResultByName("expires_utc")) / 1000;
- Services.cookies.add(host_key,
- row.getResultByName("path"),
- row.getResultByName("name"),
- row.getResultByName("value"),
- row.getResultByName("secure"),
- row.getResultByName("httponly"),
- false,
- parseInt(expiresUtc),
- {});
- } catch (e) {
- Cu.reportError(e);
- }
- }
- aCallback(true);
- }),
- };
-}
-
-function GetWindowsPasswordsResource(aProfileFolder) {
- let loginFile = aProfileFolder.clone();
- loginFile.append("Login Data");
- if (!loginFile.exists())
- return null;
-
- return {
- type: MigrationUtils.resourceTypes.PASSWORDS,
-
- migrate: Task.async(function* (aCallback) {
- let rows = yield MigrationUtils.getRowsFromDBWithoutLocks(loginFile.path, "Chrome passwords",
- `SELECT origin_url, action_url, username_element, username_value,
- password_element, password_value, signon_realm, scheme, date_created,
- times_used FROM logins WHERE blacklisted_by_user = 0`).catch(ex => {
- Cu.reportError(ex);
- aCallback(false);
- });
- // If the promise was rejected we will have already called aCallback,
- // so we can just return here.
- if (!rows) {
- return;
- }
- let crypto = new OSCrypto();
-
- for (let row of rows) {
- try {
- let origin_url = NetUtil.newURI(row.getResultByName("origin_url"));
- // Ignore entries for non-http(s)/ftp URLs because we likely can't
- // use them anyway.
- const kValidSchemes = new Set(["https", "http", "ftp"]);
- if (!kValidSchemes.has(origin_url.scheme)) {
- continue;
- }
- let loginInfo = {
- username: row.getResultByName("username_value"),
- password: crypto.
- decryptData(crypto.arrayToString(row.getResultByName("password_value")),
- null),
- hostname: origin_url.prePath,
- formSubmitURL: null,
- httpRealm: null,
- usernameElement: row.getResultByName("username_element"),
- passwordElement: row.getResultByName("password_element"),
- timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
- timesUsed: row.getResultByName("times_used") + 0,
- };
-
- switch (row.getResultByName("scheme")) {
- case AUTH_TYPE.SCHEME_HTML:
- let action_url = NetUtil.newURI(row.getResultByName("action_url"));
- if (!kValidSchemes.has(action_url.scheme)) {
- continue; // This continues the outer for loop.
- }
- loginInfo.formSubmitURL = action_url.prePath;
- break;
- case AUTH_TYPE.SCHEME_BASIC:
- case AUTH_TYPE.SCHEME_DIGEST:
- // signon_realm format is URIrealm, so we need remove URI
- loginInfo.httpRealm = row.getResultByName("signon_realm")
- .substring(loginInfo.hostname.length + 1);
- break;
- default:
- throw new Error("Login data scheme type not supported: " +
- row.getResultByName("scheme"));
- }
- MigrationUtils.insertLoginWrapper(loginInfo);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- crypto.finalize();
- aCallback(true);
- }),
- };
-}
-
-ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
-ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
-ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
-
-
-/**
- * Chromium migration
- **/
-function ChromiumProfileMigrator() {
- let chromiumUserDataFolder = getDataFolder(["Chromium"], ["Chromium"], ["chromium"]);
- this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null;
-}
-
-ChromiumProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
-ChromiumProfileMigrator.prototype.classDescription = "Chromium Profile Migrator";
-ChromiumProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
-ChromiumProfileMigrator.prototype.classID = Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}");
-
-var componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];
-
-/**
- * Chrome Canary
- * Not available on Linux
- **/
-function CanaryProfileMigrator() {
- let chromeUserDataFolder = getDataFolder(["Google", "Chrome SxS"], ["Google", "Chrome Canary"]);
- this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null;
-}
-CanaryProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
-CanaryProfileMigrator.prototype.classDescription = "Chrome Canary Profile Migrator";
-CanaryProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=canary";
-CanaryProfileMigrator.prototype.classID = Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}");
-
-if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
- componentsArray.push(CanaryProfileMigrator);
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory(componentsArray);
diff --git a/browser/components/migration/ESEDBReader.jsm b/browser/components/migration/ESEDBReader.jsm
deleted file mode 100644
index 0768c65aa..000000000
--- a/browser/components/migration/ESEDBReader.jsm
+++ /dev/null
@@ -1,590 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["ESEDBReader"]; /* exported ESEDBReader */
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/ctypes.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyGetter(this, "log", () => {
- let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
- let consoleOptions = {
- maxLogLevelPref: "browser.esedbreader.loglevel",
- prefix: "ESEDBReader",
- };
- return new ConsoleAPI(consoleOptions);
-});
-
-// We have a globally unique identifier for ESE instances. A new one
-// is used for each different database opened.
-let gESEInstanceCounter = 0;
-
-// We limit the length of strings that we read from databases.
-const MAX_STR_LENGTH = 64 * 1024;
-
-// Kernel-related types:
-const KERNEL = {};
-KERNEL.FILETIME = new ctypes.StructType("FILETIME", [
- {dwLowDateTime: ctypes.uint32_t},
- {dwHighDateTime: ctypes.uint32_t}
-]);
-KERNEL.SYSTEMTIME = new ctypes.StructType("SYSTEMTIME", [
- {wYear: ctypes.uint16_t},
- {wMonth: ctypes.uint16_t},
- {wDayOfWeek: ctypes.uint16_t},
- {wDay: ctypes.uint16_t},
- {wHour: ctypes.uint16_t},
- {wMinute: ctypes.uint16_t},
- {wSecond: ctypes.uint16_t},
- {wMilliseconds: ctypes.uint16_t}
-]);
-
-// DB column types, cribbed from the ESE header
-var COLUMN_TYPES = {
- JET_coltypBit: 1, /* True, False, or NULL */
- JET_coltypUnsignedByte: 2, /* 1-byte integer, unsigned */
- JET_coltypShort: 3, /* 2-byte integer, signed */
- JET_coltypLong: 4, /* 4-byte integer, signed */
- JET_coltypCurrency: 5, /* 8 byte integer, signed */
- JET_coltypIEEESingle: 6, /* 4-byte IEEE single precision */
- JET_coltypIEEEDouble: 7, /* 8-byte IEEE double precision */
- JET_coltypDateTime: 8, /* Integral date, fractional time */
- JET_coltypBinary: 9, /* Binary data, < 255 bytes */
- JET_coltypText: 10, /* ANSI text, case insensitive, < 255 bytes */
- JET_coltypLongBinary: 11, /* Binary data, long value */
- JET_coltypLongText: 12, /* ANSI text, long value */
-
- JET_coltypUnsignedLong: 14, /* 4-byte unsigned integer */
- JET_coltypLongLong: 15, /* 8-byte signed integer */
- JET_coltypGUID: 16, /* 16-byte globally unique identifier */
-};
-
-// Not very efficient, but only used for error messages
-function getColTypeName(numericValue) {
- return Object.keys(COLUMN_TYPES).find(t => COLUMN_TYPES[t] == numericValue) || "unknown";
-}
-
-// All type constants and method wrappers go on this object:
-const ESE = {};
-ESE.JET_ERR = ctypes.long;
-ESE.JET_PCWSTR = ctypes.char16_t.ptr;
-// The ESE header calls this JET_API_PTR, but because it isn't ever used as a
-// pointer and because OS.File code implies that the name you give a type
-// matters, I opted for a different name.
-// Note that this is defined differently on 32 vs. 64-bit in the header.
-ESE.JET_API_ITEM = ctypes.voidptr_t.size == 4 ? ctypes.unsigned_long : ctypes.uint64_t;
-ESE.JET_INSTANCE = ESE.JET_API_ITEM;
-ESE.JET_SESID = ESE.JET_API_ITEM;
-ESE.JET_TABLEID = ESE.JET_API_ITEM;
-ESE.JET_COLUMNID = ctypes.unsigned_long;
-ESE.JET_GRBIT = ctypes.unsigned_long;
-ESE.JET_COLTYP = ctypes.unsigned_long;
-ESE.JET_DBID = ctypes.unsigned_long;
-
-ESE.JET_COLUMNDEF = new ctypes.StructType("JET_COLUMNDEF", [
- {"cbStruct": ctypes.unsigned_long},
- {"columnid": ESE.JET_COLUMNID },
- {"coltyp": ESE.JET_COLTYP },
- {"wCountry": ctypes.unsigned_short }, // sepcifies the country/region for the column definition
- {"langid": ctypes.unsigned_short },
- {"cp": ctypes.unsigned_short },
- {"wCollate": ctypes.unsigned_short }, /* Must be 0 */
- {"cbMax": ctypes.unsigned_long },
- {"grbit": ESE.JET_GRBIT }
-]);
-
-// Track open databases
-let gOpenDBs = new Map();
-
-// Track open libraries
-let gLibs = {};
-this.ESE = ESE; // Required for tests.
-this.KERNEL = KERNEL; // ditto
-this.gLibs = gLibs; // ditto
-
-function convertESEError(errorCode) {
- switch (errorCode) {
- case -1213 /* JET_errPageSizeMismatch */:
- case -1002 /* JET_errInvalidName*/:
- case -1507 /* JET_errColumnNotFound */:
- // The DB format has changed and we haven't updated this migration code:
- return "The database format has changed, error code: " + errorCode;
- case -1207 /* JET_errDatabaseLocked */:
- case -1302 /* JET_errTableLocked */:
- return "The database or table is locked, error code: " + errorCode;
- case -1809 /* JET_errPermissionDenied*/:
- case -1907 /* JET_errAccessDenied */:
- return "Access or permission denied, error code: " + errorCode;
- case -1044 /* JET_errInvalidFilename */:
- return "Invalid file name";
- case -1811 /* JET_errFileNotFound */:
- return "File not found";
- case -550 /* JET_errDatabaseDirtyShutdown */:
- return "Database in dirty shutdown state (without the requisite logs?)";
- case -514 /* JET_errBadLogVersion */:
- return "Database log version does not match the version of ESE in use.";
- default:
- return "Unknown error: " + errorCode;
- }
-}
-
-function handleESEError(method, methodName, shouldThrow = true, errorLog = true) {
- return function () {
- let rv;
- try {
- rv = method.apply(null, arguments);
- } catch (ex) {
- log.error("Error calling into ctypes method", methodName, ex);
- throw ex;
- }
- let resultCode = parseInt(rv.toString(10), 10);
- if (resultCode < 0) {
- if (errorLog) {
- log.error("Got error " + resultCode + " calling " + methodName);
- }
- if (shouldThrow) {
- throw new Error(convertESEError(rv));
- }
- } else if (resultCode > 0 && errorLog) {
- log.warn("Got warning " + resultCode + " calling " + methodName);
- }
- return resultCode;
- };
-}
-
-
-function declareESEFunction(methodName, ...args) {
- let declaration = ["Jet" + methodName, ctypes.winapi_abi, ESE.JET_ERR].concat(args);
- let ctypeMethod = gLibs.ese.declare.apply(gLibs.ese, declaration);
- ESE[methodName] = handleESEError(ctypeMethod, methodName);
- ESE["FailSafe" + methodName] = handleESEError(ctypeMethod, methodName, false);
- ESE["Manual" + methodName] = handleESEError(ctypeMethod, methodName, false, false);
-}
-
-function declareESEFunctions() {
- declareESEFunction("GetDatabaseFileInfoW", ESE.JET_PCWSTR, ctypes.voidptr_t,
- ctypes.unsigned_long, ctypes.unsigned_long);
-
- declareESEFunction("GetSystemParameterW", ESE.JET_INSTANCE, ESE.JET_SESID,
- ctypes.unsigned_long, ESE.JET_API_ITEM.ptr,
- ESE.JET_PCWSTR, ctypes.unsigned_long);
- declareESEFunction("SetSystemParameterW", ESE.JET_INSTANCE.ptr,
- ESE.JET_SESID, ctypes.unsigned_long, ESE.JET_API_ITEM,
- ESE.JET_PCWSTR);
- declareESEFunction("CreateInstanceW", ESE.JET_INSTANCE.ptr, ESE.JET_PCWSTR);
- declareESEFunction("Init", ESE.JET_INSTANCE.ptr);
-
- declareESEFunction("BeginSessionW", ESE.JET_INSTANCE, ESE.JET_SESID.ptr,
- ESE.JET_PCWSTR, ESE.JET_PCWSTR);
- declareESEFunction("AttachDatabaseW", ESE.JET_SESID, ESE.JET_PCWSTR,
- ESE.JET_GRBIT);
- declareESEFunction("DetachDatabaseW", ESE.JET_SESID, ESE.JET_PCWSTR);
- declareESEFunction("OpenDatabaseW", ESE.JET_SESID, ESE.JET_PCWSTR,
- ESE.JET_PCWSTR, ESE.JET_DBID.ptr, ESE.JET_GRBIT);
- declareESEFunction("OpenTableW", ESE.JET_SESID, ESE.JET_DBID, ESE.JET_PCWSTR,
- ctypes.voidptr_t, ctypes.unsigned_long, ESE.JET_GRBIT,
- ESE.JET_TABLEID.ptr);
-
- declareESEFunction("GetColumnInfoW", ESE.JET_SESID, ESE.JET_DBID,
- ESE.JET_PCWSTR, ESE.JET_PCWSTR, ctypes.voidptr_t,
- ctypes.unsigned_long, ctypes.unsigned_long);
-
- declareESEFunction("Move", ESE.JET_SESID, ESE.JET_TABLEID, ctypes.long,
- ESE.JET_GRBIT);
-
- declareESEFunction("RetrieveColumn", ESE.JET_SESID, ESE.JET_TABLEID,
- ESE.JET_COLUMNID, ctypes.voidptr_t, ctypes.unsigned_long,
- ctypes.unsigned_long.ptr, ESE.JET_GRBIT, ctypes.voidptr_t);
-
- declareESEFunction("CloseTable", ESE.JET_SESID, ESE.JET_TABLEID);
- declareESEFunction("CloseDatabase", ESE.JET_SESID, ESE.JET_DBID,
- ESE.JET_GRBIT);
-
- declareESEFunction("EndSession", ESE.JET_SESID, ESE.JET_GRBIT);
-
- declareESEFunction("Term", ESE.JET_INSTANCE);
-}
-
-function unloadLibraries() {
- log.debug("Unloading");
- if (gOpenDBs.size) {
- log.error("Shouldn't unload libraries before DBs are closed!");
- for (let db of gOpenDBs.values()) {
- db._close();
- }
- }
- for (let k of Object.keys(ESE)) {
- delete ESE[k];
- }
- gLibs.ese.close();
- gLibs.kernel.close();
- delete gLibs.ese;
- delete gLibs.kernel;
-}
-
-function loadLibraries() {
- Services.obs.addObserver(unloadLibraries, "xpcom-shutdown", false);
- gLibs.ese = ctypes.open("esent.dll");
- gLibs.kernel = ctypes.open("kernel32.dll");
- KERNEL.FileTimeToSystemTime = gLibs.kernel.declare("FileTimeToSystemTime",
- ctypes.default_abi, ctypes.int, KERNEL.FILETIME.ptr, KERNEL.SYSTEMTIME.ptr);
-
- declareESEFunctions();
-}
-
-function ESEDB(rootPath, dbPath, logPath) {
- log.info("Created db");
- this.rootPath = rootPath;
- this.dbPath = dbPath;
- this.logPath = logPath;
- this._references = 0;
- this._init();
-}
-
-ESEDB.prototype = {
- rootPath: null,
- dbPath: null,
- logPath: null,
- _opened: false,
- _attached: false,
- _sessionCreated: false,
- _instanceCreated: false,
- _dbId: null,
- _sessionId: null,
- _instanceId: null,
-
- _init() {
- if (!gLibs.ese) {
- loadLibraries();
- }
- this.incrementReferenceCounter();
- this._internalOpen();
- },
-
- _internalOpen() {
- try {
- let dbinfo = new ctypes.unsigned_long();
- ESE.GetDatabaseFileInfoW(this.dbPath, dbinfo.address(),
- ctypes.unsigned_long.size, 17);
-
- let pageSize = ctypes.UInt64.lo(dbinfo.value);
- ESE.SetSystemParameterW(null, 0, 64 /* JET_paramDatabasePageSize*/,
- pageSize, null);
-
- this._instanceId = new ESE.JET_INSTANCE();
- ESE.CreateInstanceW(this._instanceId.address(),
- "firefox-dbreader-" + (gESEInstanceCounter++));
- this._instanceCreated = true;
-
- ESE.SetSystemParameterW(this._instanceId.address(), 0,
- 0 /* JET_paramSystemPath*/, 0, this.rootPath);
- ESE.SetSystemParameterW(this._instanceId.address(), 0,
- 1 /* JET_paramTempPath */, 0, this.rootPath);
- ESE.SetSystemParameterW(this._instanceId.address(), 0,
- 2 /* JET_paramLogFilePath*/, 0, this.logPath);
-
- // Shouldn't try to call JetTerm if the following call fails.
- this._instanceCreated = false;
- ESE.Init(this._instanceId.address());
- this._instanceCreated = true;
- this._sessionId = new ESE.JET_SESID();
- ESE.BeginSessionW(this._instanceId, this._sessionId.address(), null,
- null);
- this._sessionCreated = true;
-
- const JET_bitDbReadOnly = 1;
- ESE.AttachDatabaseW(this._sessionId, this.dbPath, JET_bitDbReadOnly);
- this._attached = true;
- this._dbId = new ESE.JET_DBID();
- ESE.OpenDatabaseW(this._sessionId, this.dbPath, null,
- this._dbId.address(), JET_bitDbReadOnly);
- this._opened = true;
- } catch (ex) {
- try {
- this._close();
- } catch (innerException) {
- Cu.reportError(innerException);
- }
- // Make sure caller knows we failed.
- throw ex;
- }
- gOpenDBs.set(this.dbPath, this);
- },
-
- checkForColumn(tableName, columnName) {
- if (!this._opened) {
- throw new Error("The database was closed!");
- }
-
- let columnInfo;
- try {
- columnInfo = this._getColumnInfo(tableName, [{name: columnName}]);
- } catch (ex) {
- return null;
- }
- return columnInfo[0];
- },
-
- tableExists(tableName) {
- if (!this._opened) {
- throw new Error("The database was closed!");
- }
-
- let tableId = new ESE.JET_TABLEID();
- let rv = ESE.ManualOpenTableW(this._sessionId, this._dbId, tableName, null,
- 0, 4 /* JET_bitTableReadOnly */,
- tableId.address());
- if (rv == -1305 /* JET_errObjectNotFound */) {
- return false;
- }
- if (rv < 0) {
- log.error("Got error " + rv + " calling OpenTableW");
- throw new Error(convertESEError(rv));
- }
-
- if (rv > 0) {
- log.error("Got warning " + rv + " calling OpenTableW");
- }
- ESE.FailSafeCloseTable(this._sessionId, tableId);
- return true;
- },
-
- tableItems: function*(tableName, columns) {
- if (!this._opened) {
- throw new Error("The database was closed!");
- }
-
- let tableOpened = false;
- let tableId;
- try {
- tableId = this._openTable(tableName);
- tableOpened = true;
-
- let columnInfo = this._getColumnInfo(tableName, columns);
-
- let rv = ESE.ManualMove(this._sessionId, tableId,
- -2147483648 /* JET_MoveFirst */, 0);
- if (rv == -1603 /* JET_errNoCurrentRecord */) {
- // There are no rows in the table.
- this._closeTable(tableId);
- return;
- }
- if (rv != 0) {
- throw new Error(convertESEError(rv));
- }
-
- do {
- let rowContents = {};
- for (let column of columnInfo) {
- let [buffer, bufferSize] = this._getBufferForColumn(column);
- // We handle errors manually so we accurately deal with NULL values.
- let err = ESE.ManualRetrieveColumn(this._sessionId, tableId,
- column.id, buffer.address(),
- bufferSize, null, 0, null);
- rowContents[column.name] = this._convertResult(column, buffer, err);
- }
- yield rowContents;
- } while (ESE.ManualMove(this._sessionId, tableId, 1 /* JET_MoveNext */, 0) === 0);
- } catch (ex) {
- if (tableOpened) {
- this._closeTable(tableId);
- }
- throw ex;
- }
- this._closeTable(tableId);
- },
-
- _openTable(tableName) {
- let tableId = new ESE.JET_TABLEID();
- ESE.OpenTableW(this._sessionId, this._dbId, tableName, null,
- 0, 4 /* JET_bitTableReadOnly */, tableId.address());
- return tableId;
- },
-
- _getBufferForColumn(column) {
- let buffer;
- if (column.type == "string") {
- let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
- // size on the column is in bytes, 2 bytes to a wchar, so:
- let charCount = column.dbSize >> 1;
- buffer = new wchar_tArray(charCount);
- } else if (column.type == "boolean") {
- buffer = new ctypes.uint8_t();
- } else if (column.type == "date") {
- buffer = new KERNEL.FILETIME();
- } else if (column.type == "guid") {
- let byteArray = ctypes.ArrayType(ctypes.uint8_t);
- buffer = new byteArray(column.dbSize);
- } else {
- throw new Error("Unknown type " + column.type);
- }
- return [buffer, buffer.constructor.size];
- },
-
- _convertResult(column, buffer, err) {
- if (err != 0) {
- if (err == 1004) {
- // Deal with null values:
- buffer = null;
- } else {
- Cu.reportError("Unexpected JET error: " + err + ";" + " retrieving value for column " + column.name);
- throw new Error(convertESEError(err));
- }
- }
- if (column.type == "string") {
- return buffer ? buffer.readString() : "";
- }
- if (column.type == "boolean") {
- return buffer ? (buffer.value == 255) : false;
- }
- if (column.type == "guid") {
- if (buffer.length != 16) {
- Cu.reportError("Buffer size for guid field " + column.id + " should have been 16!");
- return "";
- }
- let rv = "{";
- for (let i = 0; i < 16; i++) {
- if (i == 4 || i == 6 || i == 8 || i == 10) {
- rv += "-";
- }
- let byteValue = buffer.addressOfElement(i).contents;
- // Ensure there's a leading 0
- rv += ("0" + byteValue.toString(16)).substr(-2);
- }
- return rv + "}";
- }
- if (column.type == "date") {
- if (!buffer) {
- return null;
- }
- let systemTime = new KERNEL.SYSTEMTIME();
- let result = KERNEL.FileTimeToSystemTime(buffer.address(), systemTime.address());
- if (result == 0) {
- throw new Error(ctypes.winLastError);
- }
-
- // System time is in UTC, so we use Date.UTC to get milliseconds from epoch,
- // then divide by 1000 to get seconds, and round down:
- return new Date(Date.UTC(systemTime.wYear,
- systemTime.wMonth - 1,
- systemTime.wDay,
- systemTime.wHour,
- systemTime.wMinute,
- systemTime.wSecond,
- systemTime.wMilliseconds));
- }
- return undefined;
- },
-
- _getColumnInfo(tableName, columns) {
- let rv = [];
- for (let column of columns) {
- let columnInfoFromDB = new ESE.JET_COLUMNDEF();
- ESE.GetColumnInfoW(this._sessionId, this._dbId, tableName, column.name,
- columnInfoFromDB.address(), ESE.JET_COLUMNDEF.size, 0 /* JET_ColInfo */);
- let dbType = parseInt(columnInfoFromDB.coltyp.toString(10), 10);
- let dbSize = parseInt(columnInfoFromDB.cbMax.toString(10), 10);
- if (column.type == "string") {
- if (dbType != COLUMN_TYPES.JET_coltypLongText &&
- dbType != COLUMN_TYPES.JET_coltypText) {
- throw new Error("Invalid column type for column " + column.name +
- "; expected text type, got type " + getColTypeName(dbType));
- }
- if (dbSize > MAX_STR_LENGTH) {
- throw new Error("Column " + column.name + " has more than 64k data in it. This API is not designed to handle data that large.");
- }
- } else if (column.type == "boolean") {
- if (dbType != COLUMN_TYPES.JET_coltypBit) {
- throw new Error("Invalid column type for column " + column.name +
- "; expected bit type, got type " + getColTypeName(dbType));
- }
- } else if (column.type == "date") {
- if (dbType != COLUMN_TYPES.JET_coltypLongLong) {
- throw new Error("Invalid column type for column " + column.name +
- "; expected long long type, got type " + getColTypeName(dbType));
- }
- } else if (column.type == "guid") {
- if (dbType != COLUMN_TYPES.JET_coltypGUID) {
- throw new Error("Invalid column type for column " + column.name +
- "; expected guid type, got type " + getColTypeName(dbType));
- }
- } else if (column.type) {
- throw new Error("Unknown column type " + column.type + " requested for column " +
- column.name + ", don't know what to do.");
- }
-
- rv.push({name: column.name, id: columnInfoFromDB.columnid, type: column.type, dbSize, dbType});
- }
- return rv;
- },
-
- _closeTable(tableId) {
- ESE.FailSafeCloseTable(this._sessionId, tableId);
- },
-
- _close() {
- this._internalClose();
- gOpenDBs.delete(this.dbPath);
- },
-
- _internalClose() {
- if (this._opened) {
- log.debug("close db");
- ESE.FailSafeCloseDatabase(this._sessionId, this._dbId, 0);
- log.debug("finished close db");
- this._opened = false;
- }
- if (this._attached) {
- log.debug("detach db");
- ESE.FailSafeDetachDatabaseW(this._sessionId, this.dbPath);
- this._attached = false;
- }
- if (this._sessionCreated) {
- log.debug("end session");
- ESE.FailSafeEndSession(this._sessionId, 0);
- this._sessionCreated = false;
- }
- if (this._instanceCreated) {
- log.debug("term");
- ESE.FailSafeTerm(this._instanceId);
- this._instanceCreated = false;
- }
- },
-
- incrementReferenceCounter() {
- this._references++;
- },
-
- decrementReferenceCounter() {
- this._references--;
- if (this._references <= 0) {
- this._close();
- }
- },
-};
-
-let ESEDBReader = {
- openDB(rootDir, dbFile, logDir) {
- let dbFilePath = dbFile.path;
- if (gOpenDBs.has(dbFilePath)) {
- let db = gOpenDBs.get(dbFilePath);
- db.incrementReferenceCounter();
- return db;
- }
- // ESE is really picky about the trailing slashes according to the docs,
- // so we do as we're told and ensure those are there:
- return new ESEDB(rootDir.path + "\\", dbFilePath, logDir.path + "\\");
- },
-
- closeDB(db) {
- db.decrementReferenceCounter();
- },
-
- COLUMN_TYPES,
-};
-
diff --git a/browser/components/migration/EdgeProfileMigrator.js b/browser/components/migration/EdgeProfileMigrator.js
deleted file mode 100644
index afdcc2773..000000000
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ /dev/null
@@ -1,450 +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/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-Cu.import("resource:///modules/MSMigrationUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ESEDBReader",
- "resource:///modules/ESEDBReader.jsm");
-
-const kEdgeRegistryRoot = "SOFTWARE\\Classes\\Local Settings\\Software\\" +
- "Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\" +
- "microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge";
-const kEdgeDatabasePath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
-
-XPCOMUtils.defineLazyGetter(this, "gEdgeDatabase", function() {
- let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
- if (!edgeDir) {
- return null;
- }
- edgeDir.appendRelativePath(kEdgeDatabasePath);
- if (!edgeDir.exists() || !edgeDir.isReadable() || !edgeDir.isDirectory()) {
- return null;
- }
- let expectedLocation = edgeDir.clone();
- expectedLocation.appendRelativePath("nouser1\\120712-0049\\DBStore\\spartan.edb");
- if (expectedLocation.exists() && expectedLocation.isReadable() && expectedLocation.isFile()) {
- return expectedLocation;
- }
- // We used to recurse into arbitrary subdirectories here, but that code
- // went unused, so it likely isn't necessary, even if we don't understand
- // where the magic folders above come from, they seem to be the same for
- // everyone. Just return null if they're not there:
- return null;
-});
-
-/**
- * Get rows from a table in the Edge DB as an array of JS objects.
- *
- * @param {String} tableName the name of the table to read.
- * @param {String[]|function} columns a list of column specifiers
- * (see ESEDBReader.jsm) or a function that
- * generates them based on the database
- * reference once opened.
- * @param {function} filterFn a function that is called for each row.
- * Only rows for which it returns a truthy
- * value are included in the result.
- * @param {nsIFile} dbFile the database file to use. Defaults to
- * the main Edge database.
- * @returns {Array} An array of row objects.
- */
-function readTableFromEdgeDB(tableName, columns, filterFn, dbFile = gEdgeDatabase) {
- let database;
- let rows = [];
- try {
- let logFile = dbFile.parent;
- logFile.append("LogFiles");
- database = ESEDBReader.openDB(dbFile.parent, dbFile, logFile);
-
- if (typeof columns == "function") {
- columns = columns(database);
- }
-
- let tableReader = database.tableItems(tableName, columns);
- for (let row of tableReader) {
- if (filterFn(row)) {
- rows.push(row);
- }
- }
- } catch (ex) {
- Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
- dbFile.path + " due to the following error: " + ex);
- // Deliberately make this fail so we expose failure in the UI:
- throw ex;
- } finally {
- if (database) {
- ESEDBReader.closeDB(database);
- }
- }
- return rows;
-}
-
-function EdgeTypedURLMigrator() {
-}
-
-EdgeTypedURLMigrator.prototype = {
- type: MigrationUtils.resourceTypes.HISTORY,
-
- get _typedURLs() {
- if (!this.__typedURLs) {
- this.__typedURLs = MSMigrationUtils.getTypedURLs(kEdgeRegistryRoot);
- }
- return this.__typedURLs;
- },
-
- get exists() {
- return this._typedURLs.size > 0;
- },
-
- migrate: function(aCallback) {
- let typedURLs = this._typedURLs;
- let places = [];
- for (let [urlString, time] of typedURLs) {
- let uri;
- try {
- uri = Services.io.newURI(urlString, null, null);
- if (["http", "https", "ftp"].indexOf(uri.scheme) == -1) {
- continue;
- }
- } catch (ex) {
- Cu.reportError(ex);
- continue;
- }
-
- // Note that the time will be in microseconds (PRTime),
- // and Date.now() returns milliseconds. Places expects PRTime,
- // so we multiply the Date.now return value to make up the difference.
- let visitDate = time || (Date.now() * 1000);
- places.push({
- uri,
- visits: [{ transitionType: Ci.nsINavHistoryService.TRANSITION_TYPED,
- visitDate}]
- });
- }
-
- if (places.length == 0) {
- aCallback(typedURLs.size == 0);
- return;
- }
-
- MigrationUtils.insertVisitsWrapper(places, {
- _success: false,
- handleResult: function() {
- // Importing any entry is considered a successful import.
- this._success = true;
- },
- handleError: function() {},
- handleCompletion: function() {
- aCallback(this._success);
- }
- });
- },
-};
-
-function EdgeReadingListMigrator() {
-}
-
-EdgeReadingListMigrator.prototype = {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- get exists() {
- return !!gEdgeDatabase;
- },
-
- migrate(callback) {
- this._migrateReadingList(PlacesUtils.bookmarks.menuGuid).then(
- () => callback(true),
- ex => {
- Cu.reportError(ex);
- callback(false);
- }
- );
- },
-
- _migrateReadingList: Task.async(function*(parentGuid) {
- let columnFn = db => {
- let columns = [
- {name: "URL", type: "string"},
- {name: "Title", type: "string"},
- {name: "AddedDate", type: "date"}
- ];
-
- // Later versions have an IsDeleted column:
- let isDeletedColumn = db.checkForColumn("ReadingList", "IsDeleted");
- if (isDeletedColumn && isDeletedColumn.dbType == ESEDBReader.COLUMN_TYPES.JET_coltypBit) {
- columns.push({name: "IsDeleted", type: "boolean"});
- }
- return columns;
- };
-
- let filterFn = row => {
- return !row.IsDeleted;
- };
-
- let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, filterFn);
- if (!readingListItems.length) {
- return;
- }
-
- let destFolderGuid = yield this._ensureReadingListFolder(parentGuid);
- let exceptionThrown;
- for (let item of readingListItems) {
- let dateAdded = item.AddedDate || new Date();
- yield MigrationUtils.insertBookmarkWrapper({
- parentGuid: destFolderGuid, url: item.URL, title: item.Title, dateAdded
- }).catch(ex => {
- if (!exceptionThrown) {
- exceptionThrown = ex;
- }
- Cu.reportError(ex);
- });
- }
- if (exceptionThrown) {
- throw exceptionThrown;
- }
- }),
-
- _ensureReadingListFolder: Task.async(function*(parentGuid) {
- if (!this.__readingListFolderGuid) {
- let folderTitle = MigrationUtils.getLocalizedString("importedEdgeReadingList");
- let folderSpec = {type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title: folderTitle};
- this.__readingListFolderGuid = (yield MigrationUtils.insertBookmarkWrapper(folderSpec)).guid;
- }
- return this.__readingListFolderGuid;
- }),
-};
-
-function EdgeBookmarksMigrator(dbOverride) {
- this.dbOverride = dbOverride;
-}
-
-EdgeBookmarksMigrator.prototype = {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- get db() { return this.dbOverride || gEdgeDatabase },
-
- get TABLE_NAME() { return "Favorites" },
-
- get exists() {
- if (!("_exists" in this)) {
- this._exists = !!this.db;
- }
- return this._exists;
- },
-
- migrate(callback) {
- this._migrateBookmarks(PlacesUtils.bookmarks.menuGuid).then(
- () => callback(true),
- ex => {
- Cu.reportError(ex);
- callback(false);
- }
- );
- },
-
- _migrateBookmarks: Task.async(function*(rootGuid) {
- let {bookmarks, folderMap} = this._fetchBookmarksFromDB();
- if (!bookmarks.length) {
- return;
- }
- yield this._importBookmarks(bookmarks, folderMap, rootGuid);
- }),
-
- _importBookmarks: Task.async(function*(bookmarks, folderMap, rootGuid) {
- if (!MigrationUtils.isStartupMigration) {
- rootGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Edge", rootGuid);
- }
-
- let exceptionThrown;
- for (let bookmark of bookmarks) {
- // If this is a folder, we might have created it already to put other bookmarks in.
- if (bookmark.IsFolder && bookmark._guid) {
- continue;
- }
-
- // If this is a folder, just create folders up to and including that folder.
- // Otherwise, create folders until we have a parent for this bookmark.
- // This avoids duplicating logic for the bookmarks bar.
- let folderId = bookmark.IsFolder ? bookmark.ItemId : bookmark.ParentId;
- let parentGuid = yield this._getGuidForFolder(folderId, folderMap, rootGuid).catch(ex => {
- if (!exceptionThrown) {
- exceptionThrown = ex;
- }
- Cu.reportError(ex);
- });
-
- // If this was a folder, we're done with this item
- if (bookmark.IsFolder) {
- continue;
- }
-
- if (!parentGuid) {
- // If we couldn't sort out a parent, fall back to importing on the root:
- parentGuid = rootGuid;
- }
- let placesInfo = {
- parentGuid,
- url: bookmark.URL,
- dateAdded: bookmark.DateUpdated || new Date(),
- title: bookmark.Title,
- };
-
- yield MigrationUtils.insertBookmarkWrapper(placesInfo).catch(ex => {
- if (!exceptionThrown) {
- exceptionThrown = ex;
- }
- Cu.reportError(ex);
- });
- }
-
- if (exceptionThrown) {
- throw exceptionThrown;
- }
- }),
-
- _fetchBookmarksFromDB() {
- let folderMap = new Map();
- let columns = [
- {name: "URL", type: "string"},
- {name: "Title", type: "string"},
- {name: "DateUpdated", type: "date"},
- {name: "IsFolder", type: "boolean"},
- {name: "IsDeleted", type: "boolean"},
- {name: "ParentId", type: "guid"},
- {name: "ItemId", type: "guid"}
- ];
- let filterFn = row => {
- if (row.IsDeleted) {
- return false;
- }
- if (row.IsFolder) {
- folderMap.set(row.ItemId, row);
- }
- return true;
- };
- let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn, this.db);
- return {bookmarks, folderMap};
- },
-
- _getGuidForFolder: Task.async(function*(folderId, folderMap, rootGuid) {
- // If the folderId is not known as a folder in the folder map, we assume
- // we just need the root
- if (!folderMap.has(folderId)) {
- return rootGuid;
- }
- let folder = folderMap.get(folderId);
- // If the folder already has a places guid, just return that.
- if (folder._guid) {
- return folder._guid;
- }
-
- // Hacks! The bookmarks bar is special:
- if (folder.Title == "_Favorites_Bar_") {
- let toolbarGuid = PlacesUtils.bookmarks.toolbarGuid;
- if (!MigrationUtils.isStartupMigration) {
- toolbarGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Edge", toolbarGuid);
- }
- folder._guid = toolbarGuid;
- return folder._guid;
- }
- // Otherwise, get the right parent guid recursively:
- let parentGuid = yield this._getGuidForFolder(folder.ParentId, folderMap, rootGuid);
- let folderInfo = {
- title: folder.Title,
- type: PlacesUtils.bookmarks.TYPE_FOLDER,
- dateAdded: folder.DateUpdated || new Date(),
- parentGuid,
- };
- // and add ourselves as a kid, and return the guid we got.
- let parentBM = yield MigrationUtils.insertBookmarkWrapper(folderInfo);
- folder._guid = parentBM.guid;
- return folder._guid;
- }),
-};
-
-function EdgeProfileMigrator() {
- this.wrappedJSObject = this;
-}
-
-EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-EdgeProfileMigrator.prototype.getESEMigratorForTesting = function(dbOverride) {
- return new EdgeBookmarksMigrator(dbOverride);
-};
-
-EdgeProfileMigrator.prototype.getResources = function() {
- let resources = [
- new EdgeBookmarksMigrator(),
- MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
- new EdgeTypedURLMigrator(),
- new EdgeReadingListMigrator(),
- ];
- let windowsVaultFormPasswordsMigrator =
- MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
- windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
- resources.push(windowsVaultFormPasswordsMigrator);
- return resources.filter(r => r.exists);
-};
-
-EdgeProfileMigrator.prototype.getLastUsedDate = function() {
- // Don't do this if we don't have a single profile (see the comment for
- // sourceProfiles) or if we can't find the database file:
- if (this.sourceProfiles !== null || !gEdgeDatabase) {
- return Promise.resolve(new Date(0));
- }
- let logFilePath = OS.Path.join(gEdgeDatabase.parent.path, "LogFiles", "edb.log");
- let dbPath = gEdgeDatabase.path;
- let cookieMigrator = MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE);
- let cookiePaths = cookieMigrator._cookiesFolders.map(f => f.path);
- let datePromises = [logFilePath, dbPath, ... cookiePaths].map(path => {
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
- });
- datePromises.push(new Promise(resolve => {
- let typedURLs = new Map();
- try {
- typedURLs = MSMigrationUtils.getTypedURLs(kEdgeRegistryRoot);
- } catch (ex) {}
- let times = [0, ... typedURLs.values()];
- resolve(Math.max.apply(Math, times));
- }));
- return Promise.all(datePromises).then(dates => {
- return new Date(Math.max.apply(Math, dates));
- });
-};
-
-/* Somewhat counterintuitively, this returns:
- * - |null| to indicate "There is only 1 (default) profile" (on win10+)
- * - |[]| to indicate "There are no profiles" (on <=win8.1) which will avoid using this migrator.
- * See MigrationUtils.jsm for slightly more info on how sourceProfiles is used.
- */
-EdgeProfileMigrator.prototype.__defineGetter__("sourceProfiles", function() {
- let isWin10OrHigher = AppConstants.isPlatformAndVersionAtLeast("win", "10");
- return isWin10OrHigher ? null : [];
-});
-
-EdgeProfileMigrator.prototype.__defineGetter__("sourceLocked", function() {
- // There is an exclusive lock on some databases. Assume they are locked for now.
- return true;
-});
-
-
-EdgeProfileMigrator.prototype.classDescription = "Edge Profile Migrator";
-EdgeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=edge";
-EdgeProfileMigrator.prototype.classID = Components.ID("{62e8834b-2d17-49f5-96ff-56344903a2ae}");
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EdgeProfileMigrator]);
diff --git a/browser/components/migration/FirefoxProfileMigrator.js b/browser/components/migration/FirefoxProfileMigrator.js
deleted file mode 100644
index 2714cdbcd..000000000
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ /dev/null
@@ -1,255 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * vim: sw=2 ts=2 sts=2 et */
- /* 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/. */
-
-"use strict";
-
-/*
- * Migrates from a Basilisk profile in a lossy manner in order to clean up a
- * user's profile. Data is only migrated where the benefits outweigh the
- * potential problems caused by importing undesired/invalid configurations
- * from the source profile.
- */
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
- "resource://gre/modules/PlacesBackups.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SessionMigration",
- "resource:///modules/sessionstore/SessionMigration.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
- "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
- "resource://gre/modules/FileUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
- "resource://gre/modules/ProfileAge.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
- "resource://gre/modules/AppConstants.jsm");
-
-
-function FirefoxProfileMigrator() {
- this.wrappedJSObject = this; // for testing...
-}
-
-FirefoxProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-FirefoxProfileMigrator.prototype._getAllProfiles = function() {
- let allProfiles = new Map();
- let profiles =
- Components.classes["@mozilla.org/toolkit/profile-service;1"]
- .getService(Components.interfaces.nsIToolkitProfileService)
- .profiles;
- while (profiles.hasMoreElements()) {
- let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
- let rootDir = profile.rootDir;
-
- if (rootDir.exists() && rootDir.isReadable() &&
- !rootDir.equals(MigrationUtils.profileStartup.directory)) {
- allProfiles.set(profile.name, rootDir);
- }
- }
- return allProfiles;
-};
-
-function sorter(a, b) {
- return a.id.toLocaleLowerCase().localeCompare(b.id.toLocaleLowerCase());
-}
-
-Object.defineProperty(FirefoxProfileMigrator.prototype, "sourceProfiles", {
- get: function() {
- return [...this._getAllProfiles().keys()].map(x => ({id: x, name: x})).sort(sorter);
- }
-});
-
-FirefoxProfileMigrator.prototype._getFileObject = function(dir, fileName) {
- let file = dir.clone();
- file.append(fileName);
-
- // File resources are monolithic. We don't make partial copies since
- // they are not expected to work alone. Return null to avoid trying to
- // copy non-existing files.
- return file.exists() ? file : null;
-};
-
-FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
- let sourceProfileDir = aProfile ? this._getAllProfiles().get(aProfile.id) :
- Components.classes["@mozilla.org/toolkit/profile-service;1"]
- .getService(Components.interfaces.nsIToolkitProfileService)
- .selectedProfile.rootDir;
- if (!sourceProfileDir || !sourceProfileDir.exists() ||
- !sourceProfileDir.isReadable())
- return null;
-
- // Being a startup-only migrator, we can rely on
- // MigrationUtils.profileStartup being set.
- let currentProfileDir = MigrationUtils.profileStartup.directory;
-
- // Surely data cannot be imported from the current profile.
- if (sourceProfileDir.equals(currentProfileDir))
- return null;
-
- return this._getResourcesInternal(sourceProfileDir, currentProfileDir);
-};
-
-FirefoxProfileMigrator.prototype.getLastUsedDate = function() {
- // We always pretend we're really old, so that we don't mess
- // up the determination of which browser is the most 'recent'
- // to import from.
- return Promise.resolve(new Date(0));
-};
-
-FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileDir, currentProfileDir) {
- let getFileResource = function(aMigrationType, aFileNames) {
- let files = [];
- for (let fileName of aFileNames) {
- let file = this._getFileObject(sourceProfileDir, fileName);
- if (file)
- files.push(file);
- }
- if (!files.length) {
- return null;
- }
- return {
- type: aMigrationType,
- migrate: function(aCallback) {
- for (let file of files) {
- file.copyTo(currentProfileDir, "");
- }
- aCallback(true);
- }
- };
- }.bind(this);
-
- let types = MigrationUtils.resourceTypes;
- let places = getFileResource(types.HISTORY, ["places.sqlite", "places.sqlite-wal"]);
- let cookies = getFileResource(types.COOKIES, ["cookies.sqlite", "cookies.sqlite-wal"]);
- let passwords = getFileResource(types.PASSWORDS,
- ["signons.sqlite", "logins.json", "key3.db",
- "signedInUser.json"]);
- let formData = getFileResource(types.FORMDATA, ["formhistory.sqlite"]);
- let bookmarksBackups = getFileResource(types.OTHERDATA,
- [PlacesBackups.profileRelativeFolderPath]);
- let dictionary = getFileResource(types.OTHERDATA, ["persdict.dat"]);
-
- let sessionCheckpoints = this._getFileObject(sourceProfileDir, "sessionCheckpoints.json");
- let sessionFile = this._getFileObject(sourceProfileDir, "sessionstore.js");
- let session;
- if (sessionFile) {
- session = {
- type: types.SESSION,
- migrate: function(aCallback) {
- sessionCheckpoints.copyTo(currentProfileDir, "sessionCheckpoints.json");
- let newSessionFile = currentProfileDir.clone();
- newSessionFile.append("sessionstore.js");
- let migrationPromise = SessionMigration.migrate(sessionFile.path, newSessionFile.path);
- migrationPromise.then(function() {
- let buildID = Services.appinfo.platformBuildID;
- let mstone = Services.appinfo.platformVersion;
- // Force the browser to one-off resume the session that we give it:
- Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
- // Reset the homepage_override prefs so that the browser doesn't override our
- // session with the "what's new" page:
- Services.prefs.setCharPref("browser.startup.homepage_override.mstone", mstone);
- Services.prefs.setCharPref("browser.startup.homepage_override.buildID", buildID);
- // It's too early in startup for the pref service to have a profile directory,
- // so we have to manually tell it where to save the prefs file.
- let newPrefsFile = currentProfileDir.clone();
- newPrefsFile.append("prefs.js");
- Services.prefs.savePrefFile(newPrefsFile);
- aCallback(true);
- }, function() {
- aCallback(false);
- });
- }
- };
- }
-
- // Telemetry related migrations.
- let times = {
- name: "times", // name is used only by tests.
- type: types.OTHERDATA,
- migrate: aCallback => {
- let file = this._getFileObject(sourceProfileDir, "times.json");
- if (file) {
- file.copyTo(currentProfileDir, "");
- }
- // And record the fact a migration (ie, a reset) happened.
- let timesAccessor = new ProfileAge(currentProfileDir.path);
- timesAccessor.recordProfileReset().then(
- () => aCallback(true),
- () => aCallback(false)
- );
- }
- };
- let telemetry = {
- name: "telemetry", // name is used only by tests...
- type: types.OTHERDATA,
- migrate: aCallback => {
- let createSubDir = (name) => {
- let dir = currentProfileDir.clone();
- dir.append(name);
- dir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
- return dir;
- };
-
- // If the 'datareporting' directory exists we migrate files from it.
- let haveStateFile = false;
- let dataReportingDir = this._getFileObject(sourceProfileDir, "datareporting");
- if (dataReportingDir && dataReportingDir.isDirectory()) {
- // Copy only specific files.
- let toCopy = ["state.json", "session-state.json"];
-
- let dest = createSubDir("datareporting");
- let enumerator = dataReportingDir.directoryEntries;
- while (enumerator.hasMoreElements()) {
- let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
- if (file.isDirectory() || toCopy.indexOf(file.leafName) == -1) {
- continue;
- }
-
- if (file.leafName == "state.json") {
- haveStateFile = true;
- }
- file.copyTo(dest, "");
- }
- }
-
- if (!haveStateFile) {
- // Fall back to migrating the state file that contains the client id from healthreport/.
- // We first moved the client id management from the FHR implementation to the datareporting
- // service.
- // Consequently, we try to migrate an existing FHR state file here as a fallback.
- let healthReportDir = this._getFileObject(sourceProfileDir, "healthreport");
- if (healthReportDir && healthReportDir.isDirectory()) {
- let stateFile = this._getFileObject(healthReportDir, "state.json");
- if (stateFile) {
- let dest = createSubDir("healthreport");
- stateFile.copyTo(dest, "");
- }
- }
- }
-
- aCallback(true);
- }
- };
-
- return [places, cookies, passwords, formData, dictionary, bookmarksBackups,
- session, times, telemetry].filter(r => r);
-};
-
-Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", {
- get: () => true
-});
-
-
-FirefoxProfileMigrator.prototype.classDescription = "Firefox Profile Migrator";
-FirefoxProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=firefox";
-FirefoxProfileMigrator.prototype.classID = Components.ID("{91185366-ba97-4438-acba-48deaca63386}");
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FirefoxProfileMigrator]);
diff --git a/browser/components/migration/IEProfileMigrator.js b/browser/components/migration/IEProfileMigrator.js
deleted file mode 100644
index ac055686c..000000000
--- a/browser/components/migration/IEProfileMigrator.js
+++ /dev/null
@@ -1,542 +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/. */
-
-"use strict";
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-const kLoginsKey = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
-const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-Cu.import("resource:///modules/MSMigrationUtils.jsm");
-
-
-XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
- "resource://gre/modules/ctypes.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
- "resource://gre/modules/OSCrypto.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
- "resource://gre/modules/WindowsRegistry.jsm");
-
-Cu.importGlobalProperties(["URL"]);
-
-// Resources
-
-function History() {
-}
-
-History.prototype = {
- type: MigrationUtils.resourceTypes.HISTORY,
-
- get exists() {
- return true;
- },
-
- migrate: function H_migrate(aCallback) {
- let places = [];
- let typedURLs = MSMigrationUtils.getTypedURLs("Software\\Microsoft\\Internet Explorer");
- let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
- createInstance(Ci.nsISimpleEnumerator);
- while (historyEnumerator.hasMoreElements()) {
- let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
- let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
- // MSIE stores some types of URLs in its history that we don't handle,
- // like HTMLHelp and others. Since we don't properly map handling for
- // all of them we just avoid importing them.
- if (["http", "https", "ftp", "file"].indexOf(uri.scheme) == -1) {
- continue;
- }
-
- let title = entry.get("title");
- // Embed visits have no title and don't need to be imported.
- if (title.length == 0) {
- continue;
- }
-
- // The typed urls are already fixed-up, so we can use them for comparison.
- let transitionType = typedURLs.has(uri.spec) ?
- Ci.nsINavHistoryService.TRANSITION_TYPED :
- Ci.nsINavHistoryService.TRANSITION_LINK;
- // use the current date if we have no visits for this entry.
- // Note that the entry will have a time in microseconds (PRTime),
- // and Date.now() returns milliseconds. Places expects PRTime,
- // so we multiply the Date.now return value to make up the difference.
- let lastVisitTime = entry.get("time") || (Date.now() * 1000);
-
- places.push(
- { uri: uri,
- title: title,
- visits: [{ transitionType: transitionType,
- visitDate: lastVisitTime }]
- }
- );
- }
-
- // Check whether there is any history to import.
- if (places.length == 0) {
- aCallback(true);
- return;
- }
-
- MigrationUtils.insertVisitsWrapper(places, {
- _success: false,
- handleResult: function() {
- // Importing any entry is considered a successful import.
- this._success = true;
- },
- handleError: function() {},
- handleCompletion: function() {
- aCallback(this._success);
- }
- });
- }
-};
-
-// IE form password migrator supporting windows from XP until 7 and IE from 7 until 11
-function IE7FormPasswords() {
- // used to distinguish between this migrator and other passwords migrators in tests.
- this.name = "IE7FormPasswords";
-}
-
-IE7FormPasswords.prototype = {
- type: MigrationUtils.resourceTypes.PASSWORDS,
-
- get exists() {
- // work only on windows until 7
- if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
- return false;
- }
-
- try {
- let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
- let key = Cc["@mozilla.org/windows-registry-key;1"].
- createInstance(nsIWindowsRegKey);
- key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
- nsIWindowsRegKey.ACCESS_READ);
- let count = key.valueCount;
- key.close();
- return count > 0;
- } catch (e) {
- return false;
- }
- },
-
- migrate(aCallback) {
- let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
- createInstance(Ci.nsISimpleEnumerator);
- let uris = []; // the uris of the websites that are going to be migrated
- while (historyEnumerator.hasMoreElements()) {
- let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
- let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
- // MSIE stores some types of URLs in its history that we don't handle, like HTMLHelp
- // and others. Since we are not going to import the logins that are performed in these URLs
- // we can just skip them.
- if (["http", "https", "ftp"].indexOf(uri.scheme) == -1) {
- continue;
- }
-
- uris.push(uri);
- }
- this._migrateURIs(uris);
- aCallback(true);
- },
-
- /**
- * Migrate the logins that were saved for the uris arguments.
- * @param {nsIURI[]} uris - the uris that are going to be migrated.
- */
- _migrateURIs(uris) {
- this.ctypesKernelHelpers = new MSMigrationUtils.CtypesKernelHelpers();
- this._crypto = new OSCrypto();
- let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
- let key = Cc["@mozilla.org/windows-registry-key;1"].
- createInstance(nsIWindowsRegKey);
- key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
- nsIWindowsRegKey.ACCESS_READ);
-
- let urlsSet = new Set(); // set of the already processed urls.
- // number of the successfully decrypted registry values
- let successfullyDecryptedValues = 0;
- /* The logins are stored in the registry, where the key is a hashed URL and its
- * value contains the encrypted details for all logins for that URL.
- *
- * First iterate through IE history, hashing each URL and looking for a match. If
- * found, decrypt the value, using the URL as a salt. Finally add any found logins
- * to the Firefox password manager.
- */
-
- for (let uri of uris) {
- try {
- // remove the query and the ref parts of the URL
- let urlObject = new URL(uri.spec);
- let url = urlObject.origin + urlObject.pathname;
- // if the current url is already processed, it should be skipped
- if (urlsSet.has(url)) {
- continue;
- }
- urlsSet.add(url);
- // hash value of the current uri
- let hashStr = this._crypto.getIELoginHash(url);
- if (!key.hasValue(hashStr)) {
- continue;
- }
- let value = key.readBinaryValue(hashStr);
- // if no value was found, the uri is skipped
- if (value == null) {
- continue;
- }
- let data;
- try {
- // the url is used as salt to decrypt the registry value
- data = this._crypto.decryptData(value, url, true);
- } catch (e) {
- continue;
- }
- // extract the login details from the decrypted data
- let ieLogins = this._extractDetails(data, uri);
- // if at least a credential was found in the current data, successfullyDecryptedValues should
- // be incremented by one
- if (ieLogins.length) {
- successfullyDecryptedValues++;
- }
- this._addLogins(ieLogins);
- } catch (e) {
- Cu.reportError("Error while importing logins for " + uri.spec + ": " + e);
- }
- }
- // if the number of the imported values is less than the number of values in the key, it means
- // that not all the values were imported and an error should be reported
- if (successfullyDecryptedValues < key.valueCount) {
- Cu.reportError("We failed to decrypt and import some logins. " +
- "This is likely because we didn't find the URLs where these " +
- "passwords were submitted in the IE history and which are needed to be used " +
- "as keys in the decryption.");
- }
-
- key.close();
- this._crypto.finalize();
- this.ctypesKernelHelpers.finalize();
- },
-
- _crypto: null,
-
- /**
- * Add the logins to the password manager.
- * @param {Object[]} logins - array of the login details.
- */
- _addLogins(ieLogins) {
- for (let ieLogin of ieLogins) {
- try {
- // create a new login
- let login = {
- username: ieLogin.username,
- password: ieLogin.password,
- hostname: ieLogin.url,
- timeCreated: ieLogin.creation,
- };
- MigrationUtils.insertLoginWrapper(login);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- },
-
- /**
- * Extract the details of one or more logins from the raw decrypted data.
- * @param {string} data - the decrypted data containing raw information.
- * @param {nsURI} uri - the nsURI of page where the login has occur.
- * @returns {Object[]} array of objects where each of them contains the username, password, URL,
- * and creation time representing all the logins found in the data arguments.
- */
- _extractDetails(data, uri) {
- // the structure of the header of the IE7 decrypted data for all the logins sharing the same URL
- let loginData = new ctypes.StructType("loginData", [
- // Bytes 0-3 are not needed and not documented
- {"unknown1": ctypes.uint32_t},
- // Bytes 4-7 are the header size
- {"headerSize": ctypes.uint32_t},
- // Bytes 8-11 are the data size
- {"dataSize": ctypes.uint32_t},
- // Bytes 12-19 are not needed and not documented
- {"unknown2": ctypes.uint32_t},
- {"unknown3": ctypes.uint32_t},
- // Bytes 20-23 are the data count: each username and password is considered as a data
- {"dataMax": ctypes.uint32_t},
- // Bytes 24-35 are not needed and not documented
- {"unknown4": ctypes.uint32_t},
- {"unknown5": ctypes.uint32_t},
- {"unknown6": ctypes.uint32_t}
- ]);
-
- // the structure of a IE7 decrypted login item
- let loginItem = new ctypes.StructType("loginItem", [
- // Bytes 0-3 are the offset of the username
- {"usernameOffset": ctypes.uint32_t},
- // Bytes 4-11 are the date
- {"loDateTime": ctypes.uint32_t},
- {"hiDateTime": ctypes.uint32_t},
- // Bytes 12-15 are not needed and not documented
- {"foo": ctypes.uint32_t},
- // Bytes 16-19 are the offset of the password
- {"passwordOffset": ctypes.uint32_t},
- // Bytes 20-31 are not needed and not documented
- {"unknown1": ctypes.uint32_t},
- {"unknown2": ctypes.uint32_t},
- {"unknown3": ctypes.uint32_t}
- ]);
-
- let url = uri.prePath;
- let results = [];
- let arr = this._crypto.stringToArray(data);
- // convert data to ctypes.unsigned_char.array(arr.length)
- let cdata = ctypes.unsigned_char.array(arr.length)(arr);
- // Bytes 0-35 contain the loginData data structure for all the logins sharing the same URL
- let currentLoginData = ctypes.cast(cdata, loginData);
- let headerSize = currentLoginData.headerSize;
- let currentInfoIndex = loginData.size;
- // pointer to the current login item
- let currentLoginItemPointer = ctypes.cast(cdata.addressOfElement(currentInfoIndex),
- loginItem.ptr);
- // currentLoginData.dataMax is the data count: each username and password is considered as
- // a data. So, the number of logins is the number of data dived by 2
- let numLogins = currentLoginData.dataMax / 2;
- for (let n = 0; n < numLogins; n++) {
- // Bytes 0-31 starting from currentInfoIndex contain the loginItem data structure for the
- // current login
- let currentLoginItem = currentLoginItemPointer.contents;
- let creation = this.ctypesKernelHelpers.
- fileTimeToSecondsSinceEpoch(currentLoginItem.hiDateTime,
- currentLoginItem.loDateTime) * 1000;
- let currentResult = {
- creation: creation,
- url: url,
- };
- // The username is UTF-16 and null-terminated.
- currentResult.username =
- ctypes.cast(cdata.addressOfElement(headerSize + 12 + currentLoginItem.usernameOffset),
- ctypes.char16_t.ptr).readString();
- // The password is UTF-16 and null-terminated.
- currentResult.password =
- ctypes.cast(cdata.addressOfElement(headerSize + 12 + currentLoginItem.passwordOffset),
- ctypes.char16_t.ptr).readString();
- results.push(currentResult);
- // move to the next login item
- currentLoginItemPointer = currentLoginItemPointer.increment();
- }
- return results;
- },
-};
-
-function Settings() {
-}
-
-Settings.prototype = {
- type: MigrationUtils.resourceTypes.SETTINGS,
-
- get exists() {
- return true;
- },
-
- migrate: function S_migrate(aCallback) {
- // Converts from yes/no to a boolean.
- let yesNoToBoolean = v => v == "yes";
-
- // Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
- // destination format like "en-us, ar-kw, ar-om".
- // Final string is sorted by quality (q=) param.
- function parseAcceptLanguageList(v) {
- return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
- .sort(function(a, b) {
- let qA = parseFloat(a.split(";q=")[1]) || 1.0;
- let qB = parseFloat(b.split(";q=")[1]) || 1.0;
- return qB - qA;
- })
- .map(a => a.split(";")[0]);
- }
-
- // For reference on some of the available IE Registry settings:
- // * http://msdn.microsoft.com/en-us/library/cc980058%28v=prot.13%29.aspx
- // * http://msdn.microsoft.com/en-us/library/cc980059%28v=prot.13%29.aspx
-
- // Note that only settings exposed in our UI should be migrated.
-
- this._set("Software\\Microsoft\\Internet Explorer\\International",
- "AcceptLanguage",
- "intl.accept_languages",
- parseAcceptLanguageList);
- // TODO (bug 745853): For now, only x-western font is translated.
- this._set("Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
- "IEFixedFontName",
- "font.name.monospace.x-western");
- this._set(kMainKey,
- "Use FormSuggest",
- "browser.formfill.enable",
- yesNoToBoolean);
- this._set(kMainKey,
- "FormSuggest Passwords",
- "signon.rememberSignons",
- yesNoToBoolean);
- this._set(kMainKey,
- "Anchor Underline",
- "browser.underline_anchors",
- yesNoToBoolean);
- this._set(kMainKey,
- "Display Inline Images",
- "permissions.default.image",
- v => yesNoToBoolean(v) ? 1 : 2);
- this._set(kMainKey,
- "Move System Caret",
- "accessibility.browsewithcaret",
- yesNoToBoolean);
- this._set("Software\\Microsoft\\Internet Explorer\\Settings",
- "Always Use My Colors",
- "browser.display.document_color_use",
- v => (!v ? 0 : 2));
- this._set("Software\\Microsoft\\Internet Explorer\\Settings",
- "Always Use My Font Face",
- "browser.display.use_document_fonts",
- v => !v);
- this._set(kMainKey,
- "SmoothScroll",
- "general.smoothScroll",
- Boolean);
- this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
- "WarnOnClose",
- "browser.tabs.warnOnClose",
- Boolean);
- this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
- "OpenInForeground",
- "browser.tabs.loadInBackground",
- v => !v);
-
- aCallback(true);
- },
-
- /**
- * Reads a setting from the Registry and stores the converted result into
- * the appropriate Firefox preference.
- *
- * @param aPath
- * Registry path under HKCU.
- * @param aKey
- * Name of the key.
- * @param aPref
- * Firefox preference.
- * @param [optional] aTransformFn
- * Conversion function from the Registry format to the pref format.
- */
- _set: function S__set(aPath, aKey, aPref, aTransformFn) {
- let value = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- aPath, aKey);
- // Don't import settings that have never been flipped.
- if (value === undefined)
- return;
-
- if (aTransformFn)
- value = aTransformFn(value);
-
- switch (typeof value) {
- case "string":
- Services.prefs.setCharPref(aPref, value);
- break;
- case "number":
- Services.prefs.setIntPref(aPref, value);
- break;
- case "boolean":
- Services.prefs.setBoolPref(aPref, value);
- break;
- default:
- throw new Error("Unexpected value type: " + (typeof value));
- }
- }
-};
-
-function IEProfileMigrator()
-{
- this.wrappedJSObject = this; // export this to be able to use it in the unittest.
-}
-
-IEProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-IEProfileMigrator.prototype.getResources = function IE_getResources() {
- let resources = [
- MSMigrationUtils.getBookmarksMigrator(),
- new History(),
- MSMigrationUtils.getCookiesMigrator(),
- new Settings(),
- ];
- // Only support the form password migrator for Windows XP to 7.
- if (AppConstants.isPlatformAndVersionAtMost("win", "6.1")) {
- resources.push(new IE7FormPasswords());
- }
- let windowsVaultFormPasswordsMigrator =
- MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
- windowsVaultFormPasswordsMigrator.name = "IEVaultFormPasswords";
- resources.push(windowsVaultFormPasswordsMigrator);
- return resources.filter(r => r.exists);
-};
-
-IEProfileMigrator.prototype.getLastUsedDate = function IE_getLastUsedDate() {
- let datePromises = ["Favs", "CookD"].map(dirId => {
- let {path} = Services.dirsvc.get(dirId, Ci.nsIFile);
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
- });
- datePromises.push(new Promise(resolve => {
- let typedURLs = new Map();
- try {
- typedURLs = MSMigrationUtils.getTypedURLs("Software\\Microsoft\\Internet Explorer");
- } catch (ex) {}
- let dates = [0, ... typedURLs.values()];
- resolve(Math.max.apply(Math, dates));
- }));
- return Promise.all(datePromises).then(dates => {
- return new Date(Math.max.apply(Math, dates));
- });
-};
-
-Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
- get: function IE_get_sourceHomePageURL() {
- let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
- kMainKey, "Default_Page_URL");
- let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- kMainKey, "Start Page");
- // If the user didn't customize the Start Page, he is still on the default
- // page, that may be considered the equivalent of our about:home. There's
- // no reason to retain it, since it is heavily targeted to IE.
- let homepage = startPage != defaultStartPage ? startPage : "";
-
- // IE7+ supports secondary home pages located in a REG_MULTI_SZ key. These
- // are in addition to the Start Page, and no empty entries are possible,
- // thus a Start Page is always defined if any of these exists, though it
- // may be the default one.
- let secondaryPages = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- kMainKey, "Secondary Start Pages");
- if (secondaryPages) {
- if (homepage)
- secondaryPages.unshift(homepage);
- homepage = secondaryPages.join("|");
- }
-
- return homepage;
- }
-});
-
-IEProfileMigrator.prototype.classDescription = "IE Profile Migrator";
-IEProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=ie";
-IEProfileMigrator.prototype.classID = Components.ID("{3d2532e3-4932-4774-b7ba-968f5899d3a4}");
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IEProfileMigrator]);
diff --git a/browser/components/migration/MSMigrationUtils.jsm b/browser/components/migration/MSMigrationUtils.jsm
deleted file mode 100644
index 1e0250b06..000000000
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ /dev/null
@@ -1,889 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["MSMigrationUtils"];
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm");
-
-Cu.importGlobalProperties(["FileReader"]);
-
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
- "resource://gre/modules/WindowsRegistry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
- "resource://gre/modules/ctypes.jsm");
-
-const EDGE_COOKIE_PATH_OPTIONS = ["", "#!001\\", "#!002\\"];
-const EDGE_COOKIES_SUFFIX = "MicrosoftEdge\\Cookies";
-const EDGE_FAVORITES = "AC\\MicrosoftEdge\\User\\Default\\Favorites";
-const FREE_CLOSE_FAILED = 0;
-const INTERNET_EXPLORER_EDGE_GUID = [0x3CCD5499,
- 0x4B1087A8,
- 0x886015A2,
- 0x553BDD88];
-const RESULT_SUCCESS = 0;
-const VAULT_ENUMERATE_ALL_ITEMS = 512;
-const WEB_CREDENTIALS_VAULT_ID = [0x4BF4C442,
- 0x41A09B8A,
- 0x4ADD80B3,
- 0x28DB4D70];
-
-Cu.importGlobalProperties(["File"]);
-
-const wintypes = {
- BOOL: ctypes.int,
- DWORD: ctypes.uint32_t,
- DWORDLONG: ctypes.uint64_t,
- CHAR: ctypes.char,
- PCHAR: ctypes.char.ptr,
- LPCWSTR: ctypes.char16_t.ptr,
- PDWORD: ctypes.uint32_t.ptr,
- VOIDP: ctypes.voidptr_t,
- WORD: ctypes.uint16_t,
-};
-
-// TODO: Bug 1202978 - Refactor MSMigrationUtils ctypes helpers
-function CtypesKernelHelpers() {
- this._structs = {};
- this._functions = {};
- this._libs = {};
-
- this._structs.SYSTEMTIME = new ctypes.StructType("SYSTEMTIME", [
- {wYear: wintypes.WORD},
- {wMonth: wintypes.WORD},
- {wDayOfWeek: wintypes.WORD},
- {wDay: wintypes.WORD},
- {wHour: wintypes.WORD},
- {wMinute: wintypes.WORD},
- {wSecond: wintypes.WORD},
- {wMilliseconds: wintypes.WORD}
- ]);
-
- this._structs.FILETIME = new ctypes.StructType("FILETIME", [
- {dwLowDateTime: wintypes.DWORD},
- {dwHighDateTime: wintypes.DWORD}
- ]);
-
- try {
- this._libs.kernel32 = ctypes.open("Kernel32");
-
- this._functions.FileTimeToSystemTime =
- this._libs.kernel32.declare("FileTimeToSystemTime",
- ctypes.default_abi,
- wintypes.BOOL,
- this._structs.FILETIME.ptr,
- this._structs.SYSTEMTIME.ptr);
- } catch (ex) {
- this.finalize();
- }
-}
-
-CtypesKernelHelpers.prototype = {
- /**
- * Must be invoked once after last use of any of the provided helpers.
- */
- finalize() {
- this._structs = {};
- this._functions = {};
- for (let key in this._libs) {
- let lib = this._libs[key];
- try {
- lib.close();
- } catch (ex) {}
- }
- this._libs = {};
- },
-
- /**
- * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct,
- * and then deduces the number of seconds since the epoch (which
- * is the data we want for the cookie expiry date).
- *
- * @param aTimeHi
- * Least significant DWORD.
- * @param aTimeLo
- * Most significant DWORD.
- * @return the number of seconds since the epoch
- */
- fileTimeToSecondsSinceEpoch(aTimeHi, aTimeLo) {
- let fileTime = this._structs.FILETIME();
- fileTime.dwLowDateTime = aTimeLo;
- fileTime.dwHighDateTime = aTimeHi;
- let systemTime = this._structs.SYSTEMTIME();
- let result = this._functions.FileTimeToSystemTime(fileTime.address(),
- systemTime.address());
- if (result == 0)
- throw new Error(ctypes.winLastError);
-
- // System time is in UTC, so we use Date.UTC to get milliseconds from epoch,
- // then divide by 1000 to get seconds, and round down:
- return Math.floor(Date.UTC(systemTime.wYear,
- systemTime.wMonth - 1,
- systemTime.wDay,
- systemTime.wHour,
- systemTime.wMinute,
- systemTime.wSecond,
- systemTime.wMilliseconds) / 1000);
- }
-};
-
-function CtypesVaultHelpers() {
- this._structs = {};
- this._functions = {};
-
- this._structs.GUID = new ctypes.StructType("GUID", [
- {id: wintypes.DWORD.array(4)},
- ]);
-
- this._structs.VAULT_ITEM_ELEMENT = new ctypes.StructType("VAULT_ITEM_ELEMENT", [
- // not documented
- {schemaElementId: wintypes.DWORD},
- // not documented
- {unknown1: wintypes.DWORD},
- // vault type
- {type: wintypes.DWORD},
- // not documented
- {unknown2: wintypes.DWORD},
- // value of the item
- {itemValue: wintypes.LPCWSTR},
- // not documented
- {unknown3: wintypes.CHAR.array(12)},
- ]);
-
- this._structs.VAULT_ELEMENT = new ctypes.StructType("VAULT_ELEMENT", [
- // vault item schemaId
- {schemaId: this._structs.GUID},
- // a pointer to the name of the browser VAULT_ITEM_ELEMENT
- {pszCredentialFriendlyName: wintypes.LPCWSTR},
- // a pointer to the url VAULT_ITEM_ELEMENT
- {pResourceElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
- // a pointer to the username VAULT_ITEM_ELEMENT
- {pIdentityElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
- // not documented
- {pAuthenticatorElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
- // not documented
- {pPackageSid: this._structs.VAULT_ITEM_ELEMENT.ptr},
- // time stamp in local format
- {lowLastModified: wintypes.DWORD},
- {highLastModified: wintypes.DWORD},
- // not documented
- {flags: wintypes.DWORD},
- // not documented
- {dwPropertiesCount: wintypes.DWORD},
- // not documented
- {pPropertyElements: this._structs.VAULT_ITEM_ELEMENT.ptr},
- ]);
-
- try {
- this._vaultcliLib = ctypes.open("vaultcli.dll");
-
- this._functions.VaultOpenVault =
- this._vaultcliLib.declare("VaultOpenVault",
- ctypes.winapi_abi,
- wintypes.DWORD,
- // GUID
- this._structs.GUID.ptr,
- // Flags
- wintypes.DWORD,
- // Vault Handle
- wintypes.VOIDP.ptr);
- this._functions.VaultEnumerateItems =
- this._vaultcliLib.declare("VaultEnumerateItems",
- ctypes.winapi_abi,
- wintypes.DWORD,
- // Vault Handle
- wintypes.VOIDP,
- // Flags
- wintypes.DWORD,
- // Items Count
- wintypes.PDWORD,
- // Items
- ctypes.voidptr_t);
- this._functions.VaultCloseVault =
- this._vaultcliLib.declare("VaultCloseVault",
- ctypes.winapi_abi,
- wintypes.DWORD,
- // Vault Handle
- wintypes.VOIDP);
- this._functions.VaultGetItem =
- this._vaultcliLib.declare("VaultGetItem",
- ctypes.winapi_abi,
- wintypes.DWORD,
- // Vault Handle
- wintypes.VOIDP,
- // Schema Id
- this._structs.GUID.ptr,
- // Resource
- this._structs.VAULT_ITEM_ELEMENT.ptr,
- // Identity
- this._structs.VAULT_ITEM_ELEMENT.ptr,
- // Package Sid
- this._structs.VAULT_ITEM_ELEMENT.ptr,
- // HWND Owner
- wintypes.DWORD,
- // Flags
- wintypes.DWORD,
- // Items
- this._structs.VAULT_ELEMENT.ptr.ptr);
- this._functions.VaultFree =
- this._vaultcliLib.declare("VaultFree",
- ctypes.winapi_abi,
- wintypes.DWORD,
- // Memory
- this._structs.VAULT_ELEMENT.ptr);
- } catch (ex) {
- this.finalize();
- }
-}
-
-CtypesVaultHelpers.prototype = {
- /**
- * Must be invoked once after last use of any of the provided helpers.
- */
- finalize() {
- this._structs = {};
- this._functions = {};
- try {
- this._vaultcliLib.close();
- } catch (ex) {}
- this._vaultcliLib = null;
- }
-};
-
-/**
- * Checks whether an host is an IP (v4 or v6) address.
- *
- * @param aHost
- * The host to check.
- * @return whether aHost is an IP address.
- */
-function hostIsIPAddress(aHost) {
- try {
- Services.eTLD.getBaseDomainFromHost(aHost);
- } catch (e) {
- return e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS;
- }
- return false;
-}
-
-var gEdgeDir;
-function getEdgeLocalDataFolder() {
- if (gEdgeDir) {
- return gEdgeDir.clone();
- }
- let packages = Services.dirsvc.get("LocalAppData", Ci.nsIFile);
- packages.append("Packages");
- let edgeDir = packages.clone();
- edgeDir.append("Microsoft.MicrosoftEdge_8wekyb3d8bbwe");
- try {
- if (edgeDir.exists() && edgeDir.isReadable() && edgeDir.isDirectory()) {
- gEdgeDir = edgeDir;
- return edgeDir.clone();
- }
-
- // Let's try the long way:
- let dirEntries = packages.directoryEntries;
- while (dirEntries.hasMoreElements()) {
- let subDir = dirEntries.getNext();
- subDir.QueryInterface(Ci.nsIFile);
- if (subDir.leafName.startsWith("Microsoft.MicrosoftEdge") && subDir.isReadable() &&
- subDir.isDirectory()) {
- gEdgeDir = subDir;
- return subDir.clone();
- }
- }
- } catch (ex) {
- Cu.reportError("Exception trying to find the Edge favorites directory: " + ex);
- }
- return null;
-}
-
-
-function Bookmarks(migrationType) {
- this._migrationType = migrationType;
-}
-
-Bookmarks.prototype = {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- get exists() {
- return !!this._favoritesFolder;
- },
-
- get importedAppLabel() {
- return this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE ? "IE" : "Edge";
- },
-
- __favoritesFolder: null,
- get _favoritesFolder() {
- if (!this.__favoritesFolder) {
- if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE) {
- let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile);
- if (favoritesFolder.exists() && favoritesFolder.isReadable()) {
- this.__favoritesFolder = favoritesFolder;
- }
- } else if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_EDGE) {
- let edgeDir = getEdgeLocalDataFolder();
- if (edgeDir) {
- edgeDir.appendRelativePath(EDGE_FAVORITES);
- if (edgeDir.exists() && edgeDir.isReadable() && edgeDir.isDirectory()) {
- this.__favoritesFolder = edgeDir;
- }
- }
- }
- }
- return this.__favoritesFolder;
- },
-
- __toolbarFolderName: null,
- get _toolbarFolderName() {
- if (!this.__toolbarFolderName) {
- if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE) {
- // Retrieve the name of IE's favorites subfolder that holds the bookmarks
- // in the toolbar. This was previously stored in the registry and changed
- // in IE7 to always be called "Links".
- let folderName = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- "Software\\Microsoft\\Internet Explorer\\Toolbar",
- "LinksFolderName");
- this.__toolbarFolderName = folderName || "Links";
- } else {
- this.__toolbarFolderName = "Links";
- }
- }
- return this.__toolbarFolderName;
- },
-
- migrate: function B_migrate(aCallback) {
- return Task.spawn(function* () {
- // Import to the bookmarks menu.
- let folderGuid = PlacesUtils.bookmarks.menuGuid;
- if (!MigrationUtils.isStartupMigration) {
- folderGuid =
- yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
- }
- yield this._migrateFolder(this._favoritesFolder, folderGuid);
- }.bind(this)).then(() => aCallback(true),
- e => { Cu.reportError(e); aCallback(false) });
- },
-
- _migrateFolder: Task.async(function* (aSourceFolder, aDestFolderGuid) {
- // TODO (bug 741993): the favorites order is stored in the Registry, at
- // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
- // for IE, and in a similar location for Edge.
- // Until we support it, bookmarks are imported in alphabetical order.
- let entries = aSourceFolder.directoryEntries;
- let succeeded = true;
- while (entries.hasMoreElements()) {
- let entry = entries.getNext().QueryInterface(Ci.nsIFile);
- try {
- // Make sure that entry.path == entry.target to not follow .lnk folder
- // shortcuts which could lead to infinite cycles.
- // Don't use isSymlink(), since it would throw for invalid
- // lnk files pointing to URLs or to unresolvable paths.
- if (entry.path == entry.target && entry.isDirectory()) {
- let folderGuid;
- if (entry.leafName == this._toolbarFolderName &&
- entry.parent.equals(this._favoritesFolder)) {
- // Import to the bookmarks toolbar.
- folderGuid = PlacesUtils.bookmarks.toolbarGuid;
- if (!MigrationUtils.isStartupMigration) {
- folderGuid =
- yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
- }
- }
- else {
- // Import to a new folder.
- folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- type: PlacesUtils.bookmarks.TYPE_FOLDER,
- parentGuid: aDestFolderGuid,
- title: entry.leafName
- })).guid;
- }
-
- if (entry.isReadable()) {
- // Recursively import the folder.
- yield this._migrateFolder(entry, folderGuid);
- }
- }
- else {
- // Strip the .url extension, to both check this is a valid link file,
- // and get the associated title.
- let matches = entry.leafName.match(/(.+)\.url$/i);
- if (matches) {
- let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
- getService(Ci.nsIFileProtocolHandler);
- let uri = fileHandler.readURLFile(entry);
- let title = matches[1];
-
- yield MigrationUtils.insertBookmarkWrapper({
- parentGuid: aDestFolderGuid, url: uri, title
- });
- }
- }
- } catch (ex) {
- Components.utils.reportError("Unable to import " + this.importedAppLabel + " favorite (" + entry.leafName + "): " + ex);
- succeeded = false;
- }
- }
- if (!succeeded) {
- throw new Error("Failed to import all bookmarks correctly.");
- }
- }),
-
-};
-
-function Cookies(migrationType) {
- this._migrationType = migrationType;
-}
-
-Cookies.prototype = {
- type: MigrationUtils.resourceTypes.COOKIES,
-
- get exists() {
- if (this._migrationType == MSMigrationUtils.MIGRATION_TYPE_IE) {
- return !!this._cookiesFolder;
- }
- return !!this._cookiesFolders;
- },
-
- __cookiesFolder: null,
- get _cookiesFolder() {
- // Edge stores cookies in a number of places, and this shouldn't get called:
- if (this._migrationType != MSMigrationUtils.MIGRATION_TYPE_IE) {
- throw new Error("Shouldn't be looking for a single cookie folder unless we're migrating IE");
- }
-
- // Cookies are stored in txt files, in a Cookies folder whose path varies
- // across the different OS versions. CookD takes care of most of these
- // cases, though, in Windows Vista/7, UAC makes a difference.
- // If UAC is enabled, the most common destination is CookD/Low. Though,
- // if the user runs the application in administrator mode or disables UAC,
- // cookies are stored in the original CookD destination. Cause running the
- // browser in administrator mode is unsafe and discouraged, we just care
- // about the UAC state.
- if (!this.__cookiesFolder) {
- let cookiesFolder = Services.dirsvc.get("CookD", Ci.nsIFile);
- if (cookiesFolder.exists() && cookiesFolder.isReadable()) {
- // Check if UAC is enabled.
- if (Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).userCanElevate) {
- cookiesFolder.append("Low");
- }
- this.__cookiesFolder = cookiesFolder;
- }
- }
- return this.__cookiesFolder;
- },
-
- __cookiesFolders: null,
- get _cookiesFolders() {
- if (this._migrationType != MSMigrationUtils.MIGRATION_TYPE_EDGE) {
- throw new Error("Shouldn't be looking for multiple cookie folders unless we're migrating Edge");
- }
-
- let folders = [];
- let edgeDir = getEdgeLocalDataFolder();
- if (edgeDir) {
- edgeDir.append("AC");
- for (let path of EDGE_COOKIE_PATH_OPTIONS) {
- let folder = edgeDir.clone();
- let fullPath = path + EDGE_COOKIES_SUFFIX;
- folder.appendRelativePath(fullPath);
- if (folder.exists() && folder.isReadable() && folder.isDirectory()) {
- folders.push(folder);
- }
- }
- }
- this.__cookiesFolders = folders.length ? folders : null;
- return this.__cookiesFolders;
- },
-
- migrate(aCallback) {
- this.ctypesKernelHelpers = new CtypesKernelHelpers();
-
- let cookiesGenerator = (function* genCookie() {
- let success = false;
- let folders = this._migrationType == MSMigrationUtils.MIGRATION_TYPE_EDGE ?
- this.__cookiesFolders : [this.__cookiesFolder];
- for (let folder of folders) {
- let entries = folder.directoryEntries;
- while (entries.hasMoreElements()) {
- let entry = entries.getNext().QueryInterface(Ci.nsIFile);
- // Skip eventual bogus entries.
- if (!entry.isFile() || !/\.txt$/.test(entry.leafName))
- continue;
-
- this._readCookieFile(entry, function(aSuccess) {
- // Importing even a single cookie file is considered a success.
- if (aSuccess)
- success = true;
- try {
- cookiesGenerator.next();
- } catch (ex) {}
- });
-
- yield undefined;
- }
- }
-
- this.ctypesKernelHelpers.finalize();
-
- aCallback(success);
- }).apply(this);
- cookiesGenerator.next();
- },
-
- _readCookieFile(aFile, aCallback) {
- let fileReader = new FileReader();
- let onLoadEnd = () => {
- fileReader.removeEventListener("loadend", onLoadEnd, false);
-
- if (fileReader.readyState != fileReader.DONE) {
- Cu.reportError("Could not read cookie contents: " + fileReader.error);
- aCallback(false);
- return;
- }
-
- let success = true;
- try {
- this._parseCookieBuffer(fileReader.result);
- } catch (ex) {
- Components.utils.reportError("Unable to migrate cookie: " + ex);
- success = false;
- } finally {
- aCallback(success);
- }
- };
- fileReader.addEventListener("loadend", onLoadEnd, false);
- fileReader.readAsText(File.createFromNsIFile(aFile));
- },
-
- /**
- * Parses a cookie file buffer and returns an array of the contained cookies.
- *
- * The cookie file format is a newline-separated-values with a "*" used as
- * delimeter between multiple records.
- * Each cookie has the following fields:
- * - name
- * - value
- * - host/path
- * - flags
- * - Expiration time most significant integer
- * - Expiration time least significant integer
- * - Creation time most significant integer
- * - Creation time least significant integer
- * - Record delimiter "*"
- *
- * Unfortunately, "*" can also occur inside the value of the cookie, so we
- * can't rely exclusively on it as a record separator.
- *
- * @note All the times are in FILETIME format.
- */
- _parseCookieBuffer(aTextBuffer) {
- // Note the last record is an empty string...
- let records = [];
- let lines = aTextBuffer.split("\n");
- while (lines.length > 0) {
- let record = lines.splice(0, 9);
- // ... which means this is going to be a 1-element array for that record
- if (record.length > 1) {
- records.push(record);
- }
- }
- for (let record of records) {
- let [name, value, hostpath, flags,
- expireTimeLo, expireTimeHi] = record;
-
- // IE stores deleted cookies with a zero-length value, skip them.
- if (value.length == 0)
- continue;
-
- // IE sometimes has cookies created by apps that use "~~local~~/local/file/path"
- // as the hostpath, ignore those:
- if (hostpath.startsWith("~~local~~"))
- continue;
-
- let hostLen = hostpath.indexOf("/");
- let host = hostpath.substr(0, hostLen);
- let path = hostpath.substr(hostLen);
-
- // For a non-null domain, assume it's what Mozilla considers
- // a domain cookie. See bug 222343.
- if (host.length > 0) {
- // Fist delete any possible extant matching host cookie.
- Services.cookies.remove(host, name, path, false, {});
- // Now make it a domain cookie.
- if (host[0] != "." && !hostIsIPAddress(host))
- host = "." + host;
- }
-
- // Fallback: expire in 1h (NB: time is in seconds since epoch, so we have
- // to divide the result of Date.now() (which is in milliseconds) by 1000).
- let expireTime = Math.floor(Date.now() / 1000) + 3600;
- try {
- expireTime = this.ctypesKernelHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
- Number(expireTimeLo));
- } catch (ex) {
- Cu.reportError("Failed to get expiry time for cookie for " + host);
- }
-
- Services.cookies.add(host,
- path,
- name,
- value,
- Number(flags) & 0x1, // secure
- false, // httpOnly
- false, // session
- expireTime,
- {});
- }
- }
-};
-
-function getTypedURLs(registryKeyPath) {
- // The list of typed URLs is a sort of annotation stored in the registry.
- // The number of entries stored is not UI-configurable, but has changed
- // between different Windows versions. We just keep reading up to the first
- // non-existing entry to support different limits / states of the registry.
- let typedURLs = new Map();
- let typedURLKey = Cc["@mozilla.org/windows-registry-key;1"].
- createInstance(Ci.nsIWindowsRegKey);
- let typedURLTimeKey = Cc["@mozilla.org/windows-registry-key;1"].
- createInstance(Ci.nsIWindowsRegKey);
- let cTypes = new CtypesKernelHelpers();
- try {
- typedURLKey.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- registryKeyPath + "\\TypedURLs",
- Ci.nsIWindowsRegKey.ACCESS_READ);
- try {
- typedURLTimeKey.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
- registryKeyPath + "\\TypedURLsTime",
- Ci.nsIWindowsRegKey.ACCESS_READ);
- } catch (ex) {
- typedURLTimeKey = null;
- }
- let entryName;
- for (let entry = 1; typedURLKey.hasValue((entryName = "url" + entry)); entry++) {
- let url = typedURLKey.readStringValue(entryName);
- let timeTyped = 0;
- if (typedURLTimeKey && typedURLTimeKey.hasValue(entryName)) {
- let urlTime = "";
- try {
- urlTime = typedURLTimeKey.readBinaryValue(entryName);
- } catch (ex) {
- Cu.reportError("Couldn't read url time for " + entryName);
- }
- if (urlTime.length == 8) {
- let urlTimeHex = [];
- for (let i = 0; i < 8; i++) {
- let c = urlTime.charCodeAt(i).toString(16);
- if (c.length == 1)
- c = "0" + c;
- urlTimeHex.unshift(c);
- }
- try {
- let hi = parseInt(urlTimeHex.slice(0, 4).join(""), 16);
- let lo = parseInt(urlTimeHex.slice(4, 8).join(""), 16);
- // Convert to seconds since epoch:
- timeTyped = cTypes.fileTimeToSecondsSinceEpoch(hi, lo);
- // Callers expect PRTime, which is microseconds since epoch:
- timeTyped *= 1000 * 1000;
- } catch (ex) {
- // Ignore conversion exceptions. Callers will have to deal
- // with the fallback value (0).
- }
- }
- }
- typedURLs.set(url, timeTyped);
- }
- } catch (ex) {
- Cu.reportError("Error reading typed URL history: " + ex);
- } finally {
- if (typedURLKey) {
- typedURLKey.close();
- }
- if (typedURLTimeKey) {
- typedURLTimeKey.close();
- }
- cTypes.finalize();
- }
- return typedURLs;
-}
-
-
-// Migrator for form passwords on Windows 8 and higher.
-function WindowsVaultFormPasswords () {
-}
-
-WindowsVaultFormPasswords.prototype = {
- type: MigrationUtils.resourceTypes.PASSWORDS,
-
- get exists() {
- // work only on windows 8+
- if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
- // check if there are passwords available for migration.
- return this.migrate(() => {}, true);
- }
- return false;
- },
-
- /**
- * If aOnlyCheckExists is false, import the form passwords on Windows 8 and higher from the vault
- * and then call the aCallback.
- * Otherwise, check if there are passwords in the vault.
- * @param {function} aCallback - a callback called when the migration is done.
- * @param {boolean} [aOnlyCheckExists=false] - if aOnlyCheckExists is true, just check if there are some
- * passwords to migrate. Import the passwords from the vault and call aCallback otherwise.
- * @return true if there are passwords in the vault and aOnlyCheckExists is set to true,
- * false if there is no password in the vault and aOnlyCheckExists is set to true, undefined if
- * aOnlyCheckExists is set to false.
- */
- migrate(aCallback, aOnlyCheckExists = false) {
- // check if the vault item is an IE/Edge one
- function _isIEOrEdgePassword(id) {
- return id[0] == INTERNET_EXPLORER_EDGE_GUID[0] &&
- id[1] == INTERNET_EXPLORER_EDGE_GUID[1] &&
- id[2] == INTERNET_EXPLORER_EDGE_GUID[2] &&
- id[3] == INTERNET_EXPLORER_EDGE_GUID[3];
- }
-
- let ctypesVaultHelpers = new CtypesVaultHelpers();
- let ctypesKernelHelpers = new CtypesKernelHelpers();
- let migrationSucceeded = true;
- let successfulVaultOpen = false;
- let error, vault;
- try {
- // web credentials vault id
- let vaultGuid = new ctypesVaultHelpers._structs.GUID(WEB_CREDENTIALS_VAULT_ID);
- error = new wintypes.DWORD();
- // web credentials vault
- vault = new wintypes.VOIDP();
- // open the current vault using the vaultGuid
- error = ctypesVaultHelpers._functions.VaultOpenVault(vaultGuid.address(), 0, vault.address());
- if (error != RESULT_SUCCESS) {
- throw new Error("Unable to open Vault: " + error);
- }
- successfulVaultOpen = true;
-
- let item = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr();
- let itemCount = new wintypes.DWORD();
- // enumerate all the available items. This api is going to return a table of all the
- // available items and item is going to point to the first element of this table.
- error = ctypesVaultHelpers._functions.VaultEnumerateItems(vault, VAULT_ENUMERATE_ALL_ITEMS,
- itemCount.address(),
- item.address());
- if (error != RESULT_SUCCESS) {
- throw new Error("Unable to enumerate Vault items: " + error);
- }
- for (let j = 0; j < itemCount.value; j++) {
- try {
- // if it's not an ie/edge password, skip it
- if (!_isIEOrEdgePassword(item.contents.schemaId.id)) {
- continue;
- }
- let url = item.contents.pResourceElement.contents.itemValue.readString();
- let realURL;
- try {
- realURL = Services.io.newURI(url, null, null);
- } catch (ex) { /* leave realURL as null */ }
- if (!realURL || ["http", "https", "ftp"].indexOf(realURL.scheme) == -1) {
- // Ignore items for non-URLs or URLs that aren't HTTP(S)/FTP
- continue;
- }
-
- // if aOnlyCheckExists is set to true, the purpose of the call is to return true if there is at
- // least a password which is true in this case because a password was by now already found
- if (aOnlyCheckExists) {
- return true;
- }
- let username = item.contents.pIdentityElement.contents.itemValue.readString();
- // the current login credential object
- let credential = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr();
- error = ctypesVaultHelpers._functions.VaultGetItem(vault,
- item.contents.schemaId.address(),
- item.contents.pResourceElement,
- item.contents.pIdentityElement, null,
- 0, 0, credential.address());
- if (error != RESULT_SUCCESS) {
- throw new Error("Unable to get item: " + error);
- }
-
- let password = credential.contents.pAuthenticatorElement.contents.itemValue.readString();
- let creation = Date.now();
- try {
- // login manager wants time in milliseconds since epoch, so convert
- // to seconds since epoch and multiply to get milliseconds:
- creation = ctypesKernelHelpers.
- fileTimeToSecondsSinceEpoch(item.contents.highLastModified,
- item.contents.lowLastModified) * 1000;
- } catch (ex) {
- // Ignore exceptions in the dates and just create the login for right now.
- }
- // create a new login
- let login = {
- username, password,
- hostname: realURL.prePath,
- timeCreated: creation,
- };
- MigrationUtils.insertLoginWrapper(login);
-
- // close current item
- error = ctypesVaultHelpers._functions.VaultFree(credential);
- if (error == FREE_CLOSE_FAILED) {
- throw new Error("Unable to free item: " + error);
- }
- } catch (e) {
- migrationSucceeded = false;
- Cu.reportError(e);
- } finally {
- // move to next item in the table returned by VaultEnumerateItems
- item = item.increment();
- }
- }
- } catch (e) {
- Cu.reportError(e);
- migrationSucceeded = false;
- } finally {
- if (successfulVaultOpen) {
- // close current vault
- error = ctypesVaultHelpers._functions.VaultCloseVault(vault);
- if (error == FREE_CLOSE_FAILED) {
- Cu.reportError("Unable to close vault: " + error);
- }
- }
- ctypesKernelHelpers.finalize();
- ctypesVaultHelpers.finalize();
- aCallback(migrationSucceeded);
- }
- if (aOnlyCheckExists) {
- return false;
- }
- return undefined;
- }
-};
-
-var MSMigrationUtils = {
- MIGRATION_TYPE_IE: 1,
- MIGRATION_TYPE_EDGE: 2,
- CtypesKernelHelpers: CtypesKernelHelpers,
- getBookmarksMigrator(migrationType = this.MIGRATION_TYPE_IE) {
- return new Bookmarks(migrationType);
- },
- getCookiesMigrator(migrationType = this.MIGRATION_TYPE_IE) {
- return new Cookies(migrationType);
- },
- getWindowsVaultFormPasswordsMigrator() {
- return new WindowsVaultFormPasswords();
- },
- getTypedURLs,
- getEdgeLocalDataFolder,
-};
diff --git a/browser/components/migration/MigrationUtils.jsm b/browser/components/migration/MigrationUtils.jsm
deleted file mode 100644
index e133ec520..000000000
--- a/browser/components/migration/MigrationUtils.jsm
+++ /dev/null
@@ -1,1118 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"];
-
-const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
-const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
-const TOPIC_PLACES_DEFAULTS_FINISHED = "places-browser-init-complete";
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-Cu.importGlobalProperties(["URL"]);
-
-XPCOMUtils.defineLazyModuleGetter(this, "AutoMigrate",
- "resource:///modules/AutoMigrate.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
- "resource://gre/modules/BookmarkHTMLUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
- "resource://gre/modules/LoginHelper.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
- "resource://gre/modules/PromiseUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ResponsivenessMonitor",
- "resource://gre/modules/ResponsivenessMonitor.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
- "resource://gre/modules/Sqlite.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
- "resource://gre/modules/TelemetryStopwatch.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
- "resource://gre/modules/WindowsRegistry.jsm");
-
-var gMigrators = null;
-var gProfileStartup = null;
-var gMigrationBundle = null;
-var gPreviousDefaultBrowserKey = "";
-
-let gKeepUndoData = false;
-let gUndoData = null;
-
-XPCOMUtils.defineLazyGetter(this, "gAvailableMigratorKeys", function() {
- if (AppConstants.platform == "win") {
- return [
- "firefox", "edge", "ie", "chrome", "chromium", "360se",
- "canary"
- ];
- }
- if (AppConstants.platform == "macosx") {
- return ["firefox", "safari", "chrome", "chromium", "canary"];
- }
- if (AppConstants.XP_UNIX) {
- return ["firefox", "chrome", "chromium"];
- }
- return [];
-});
-
-function getMigrationBundle() {
- if (!gMigrationBundle) {
- gMigrationBundle = Services.strings.createBundle(
- "chrome://browser/locale/migration/migration.properties");
- }
- return gMigrationBundle;
-}
-
-/**
- * Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
- *
- * To implement a migrator:
- * 1. Import this module.
- * 2. Create the prototype for the migrator, extending MigratorPrototype.
- * Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype);
- * 3. Set classDescription, contractID and classID for your migrator, and set
- * NSGetFactory appropriately.
- * 4. If the migrator supports multiple profiles, override the sourceProfiles
- * Here we default for single-profile migrator.
- * 5. Implement getResources(aProfile) (see below).
- * 6. If the migrator supports reading the home page of the source browser,
- * override |sourceHomePageURL| getter.
- * 7. For startup-only migrators, override |startupOnlyMigrator|.
- */
-this.MigratorPrototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]),
-
- /**
- * OVERRIDE IF AND ONLY IF the source supports multiple profiles.
- *
- * Returns array of profile objects from which data may be imported. The object
- * should have the following keys:
- * id - a unique string identifier for the profile
- * name - a pretty name to display to the user in the UI
- *
- * Only profiles from which data can be imported should be listed. Otherwise
- * the behavior of the migration wizard isn't well-defined.
- *
- * For a single-profile source (e.g. safari, ie), this returns null,
- * and not an empty array. That is the default implementation.
- */
- get sourceProfiles() {
- return null;
- },
-
- /**
- * MUST BE OVERRIDDEN.
- *
- * Returns an array of "migration resources" objects for the given profile,
- * or for the "default" profile, if the migrator does not support multiple
- * profiles.
- *
- * Each migration resource should provide:
- * - a |type| getter, returning any of the migration types (see
- * nsIBrowserProfileMigrator).
- *
- * - a |migrate| method, taking a single argument, aCallback(bool success),
- * for migrating the data for this resource. It may do its job
- * synchronously or asynchronously. Either way, it must call
- * aCallback(bool aSuccess) when it's done. In the case of an exception
- * thrown from |migrate|, it's taken as if aCallback(false) is called.
- *
- * Note: In the case of a simple asynchronous implementation, you may find
- * MigrationUtils.wrapMigrateFunction handy for handling aCallback easily.
- *
- * For each migration type listed in nsIBrowserProfileMigrator, multiple
- * migration resources may be provided. This practice is useful when the
- * data for a certain migration type is independently stored in few
- * locations. For example, the mac version of Safari stores its "reading list"
- * bookmarks in a separate property list.
- *
- * Note that the importation of a particular migration type is reported as
- * successful if _any_ of its resources succeeded to import (that is, called,
- * |aCallback(true)|). However, completion-status for a particular migration
- * type is reported to the UI only once all of its migrators have called
- * aCallback.
- *
- * @note The returned array should only include resources from which data
- * can be imported. So, for example, before adding a resource for the
- * BOOKMARKS migration type, you should check if you should check that the
- * bookmarks file exists.
- *
- * @param aProfile
- * The profile from which data may be imported, or an empty string
- * in the case of a single-profile migrator.
- * In the case of multiple-profiles migrator, it is guaranteed that
- * aProfile is a value returned by the sourceProfiles getter (see
- * above).
- */
- getResources: function MP_getResources(/* aProfile */) {
- throw new Error("getResources must be overridden");
- },
-
- /**
- * OVERRIDE in order to provide an estimate of when the last time was
- * that somebody used the browser. It is OK that this is somewhat fuzzy -
- * history may not be available (or be wiped or not present due to e.g.
- * incognito mode).
- *
- * @return a Promise that resolves to the last used date.
- *
- * @note If not overridden, the promise will resolve to the unix epoch.
- */
- getLastUsedDate() {
- return Promise.resolve(new Date(0));
- },
-
- /**
- * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now,
- * that is just the Firefox migrator, see bug 737381). Default: false.
- *
- * Startup-only migrators are different in two ways:
- * - they may only be used during startup.
- * - the user-profile is half baked during migration. The folder exists,
- * but it's only accessible through MigrationUtils.profileStartup.
- * The migrator can call MigrationUtils.profileStartup.doStartup
- * at any point in order to initialize the profile.
- */
- get startupOnlyMigrator() {
- return false;
- },
-
- /**
- * OVERRIDE IF AND ONLY IF your migrator supports importing the homepage.
- * @see nsIBrowserProfileMigrator
- */
- get sourceHomePageURL() {
- return "";
- },
-
- /**
- * Override if the data to migrate is locked/in-use and the user should
- * probably shutdown the source browser.
- */
- get sourceLocked() {
- return false;
- },
-
- /**
- * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
- * getResources.
- *
- * @see nsIBrowserProfileMigrator
- */
- getMigrateData: function MP_getMigrateData(aProfile) {
- let resources = this._getMaybeCachedResources(aProfile);
- if (!resources) {
- return [];
- }
- let types = resources.map(r => r.type);
- return types.reduce((a, b) => { a |= b; return a }, 0);
- },
-
- getBrowserKey: function MP_getBrowserKey() {
- return this.contractID.match(/\=([^\=]+)$/)[1];
- },
-
- /**
- * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
- * migrate for each resource.
- *
- * @see nsIBrowserProfileMigrator
- */
- migrate: function MP_migrate(aItems, aStartup, aProfile) {
- let resources = this._getMaybeCachedResources(aProfile);
- if (resources.length == 0)
- throw new Error("migrate called for a non-existent source");
-
- if (aItems != Ci.nsIBrowserProfileMigrator.ALL)
- resources = resources.filter(r => aItems & r.type);
-
- // Used to periodically give back control to the main-thread loop.
- let unblockMainThread = function() {
- return new Promise(resolve => {
- Services.tm.mainThread.dispatch(resolve, Ci.nsIThread.DISPATCH_NORMAL);
- });
- };
-
- let getHistogramIdForResourceType = (resourceType, template) => {
- if (resourceType == MigrationUtils.resourceTypes.HISTORY) {
- return template.replace("*", "HISTORY");
- }
- if (resourceType == MigrationUtils.resourceTypes.BOOKMARKS) {
- return template.replace("*", "BOOKMARKS");
- }
- if (resourceType == MigrationUtils.resourceTypes.PASSWORDS) {
- return template.replace("*", "LOGINS");
- }
- return null;
- };
-
- let browserKey = this.getBrowserKey();
-
- let maybeStartTelemetryStopwatch = resourceType => {
- let histogramId = getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_IMPORT_MS");
- if (histogramId) {
- TelemetryStopwatch.startKeyed(histogramId, browserKey);
- }
- return histogramId;
- };
-
- let maybeStartResponsivenessMonitor = resourceType => {
- let responsivenessMonitor;
- let responsivenessHistogramId =
- getHistogramIdForResourceType(resourceType, "FX_MIGRATION_*_JANK_MS");
- if (responsivenessHistogramId) {
- responsivenessMonitor = new ResponsivenessMonitor();
- }
- return {responsivenessMonitor, responsivenessHistogramId};
- };
-
- let maybeFinishResponsivenessMonitor = (responsivenessMonitor, histogramId) => {
- if (responsivenessMonitor) {
- let accumulatedDelay = responsivenessMonitor.finish();
- if (histogramId) {
- try {
- Services.telemetry.getKeyedHistogramById(histogramId)
- .add(browserKey, accumulatedDelay);
- } catch (ex) {
- Cu.reportError(histogramId + ": " + ex);
- }
- }
- }
- };
-
- let collectQuantityTelemetry = () => {
- for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
- let histogramId =
- "FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
- try {
- Services.telemetry.getKeyedHistogramById(histogramId)
- .add(browserKey, MigrationUtils._importQuantities[resourceType]);
- } catch (ex) {
- Cu.reportError(histogramId + ": " + ex);
- }
- }
- };
-
- // Called either directly or through the bookmarks import callback.
- let doMigrate = Task.async(function*() {
- let resourcesGroupedByItems = new Map();
- resources.forEach(function(resource) {
- if (!resourcesGroupedByItems.has(resource.type)) {
- resourcesGroupedByItems.set(resource.type, new Set());
- }
- resourcesGroupedByItems.get(resource.type).add(resource);
- });
-
- if (resourcesGroupedByItems.size == 0)
- throw new Error("No items to import");
-
- let notify = function(aMsg, aItemType) {
- Services.obs.notifyObservers(null, aMsg, aItemType);
- };
-
- for (let resourceType of Object.keys(MigrationUtils._importQuantities)) {
- MigrationUtils._importQuantities[resourceType] = 0;
- }
- notify("Migration:Started");
- for (let [migrationType, itemResources] of resourcesGroupedByItems) {
- notify("Migration:ItemBeforeMigrate", migrationType);
-
- let stopwatchHistogramId = maybeStartTelemetryStopwatch(migrationType);
-
- let {responsivenessMonitor, responsivenessHistogramId} =
- maybeStartResponsivenessMonitor(migrationType);
-
- let itemSuccess = false;
- for (let res of itemResources) {
- let completeDeferred = PromiseUtils.defer();
- let resourceDone = function(aSuccess) {
- itemResources.delete(res);
- itemSuccess |= aSuccess;
- if (itemResources.size == 0) {
- notify(itemSuccess ?
- "Migration:ItemAfterMigrate" : "Migration:ItemError",
- migrationType);
- resourcesGroupedByItems.delete(migrationType);
-
- if (stopwatchHistogramId) {
- TelemetryStopwatch.finishKeyed(stopwatchHistogramId, browserKey);
- }
-
- maybeFinishResponsivenessMonitor(responsivenessMonitor, responsivenessHistogramId);
-
- if (resourcesGroupedByItems.size == 0) {
- collectQuantityTelemetry();
- notify("Migration:Ended");
- }
- }
- completeDeferred.resolve();
- };
-
- // If migrate throws, an error occurred, and the callback
- // (itemMayBeDone) might haven't been called.
- try {
- res.migrate(resourceDone);
- } catch (ex) {
- Cu.reportError(ex);
- resourceDone(false);
- }
-
- // Certain resources must be ran sequentially or they could fail,
- // for example bookmarks and history (See bug 1272652).
- if (migrationType == MigrationUtils.resourceTypes.BOOKMARKS ||
- migrationType == MigrationUtils.resourceTypes.HISTORY) {
- yield completeDeferred.promise;
- }
-
- yield unblockMainThread();
- }
- }
- });
-
- if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) {
- MigrationUtils.profileStartup.doStartup();
- // First import the default bookmarks.
- // Note: We do not need to do so for the Firefox migrator
- // (=startupOnlyMigrator), as it just copies over the places database
- // from another profile.
- Task.spawn(function* () {
- // Tell nsBrowserGlue we're importing default bookmarks.
- let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].
- getService(Ci.nsIObserver);
- browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, "");
-
- // Import the default bookmarks. We ignore whether or not we succeed.
- yield BookmarkHTMLUtils.importFromURL(
- "chrome://browser/locale/bookmarks.html", true).catch(r => r);
-
- // We'll tell nsBrowserGlue we've imported bookmarks, but before that
- // we need to make sure we're going to know when it's finished
- // initializing places:
- let placesInitedPromise = new Promise(resolve => {
- let onPlacesInited = function() {
- Services.obs.removeObserver(onPlacesInited, TOPIC_PLACES_DEFAULTS_FINISHED);
- resolve();
- };
- Services.obs.addObserver(onPlacesInited, TOPIC_PLACES_DEFAULTS_FINISHED, false);
- });
- browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
- yield placesInitedPromise;
- doMigrate();
- });
- return;
- }
- doMigrate();
- },
-
- /**
- * DO NOT OVERRIDE - After deCOMing migration, this code
- * won't be part of the migrator itself.
- *
- * @see nsIBrowserProfileMigrator
- */
- get sourceExists() {
- if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration)
- return false;
-
- // For a single-profile source, check if any data is available.
- // For multiple-profiles source, make sure that at least one
- // profile is available.
- let exists = false;
- try {
- let profiles = this.sourceProfiles;
- if (!profiles) {
- let resources = this._getMaybeCachedResources("");
- if (resources && resources.length > 0)
- exists = true;
- }
- else {
- exists = profiles.length > 0;
- }
- }
- catch (ex) {
- Cu.reportError(ex);
- }
- return exists;
- },
-
- /** * PRIVATE STUFF - DO NOT OVERRIDE ***/
- _getMaybeCachedResources: function PMB__getMaybeCachedResources(aProfile) {
- let profileKey = aProfile ? aProfile.id : "";
- if (this._resourcesByProfile) {
- if (profileKey in this._resourcesByProfile)
- return this._resourcesByProfile[profileKey];
- }
- else {
- this._resourcesByProfile = { };
- }
- this._resourcesByProfile[profileKey] = this.getResources(aProfile);
- return this._resourcesByProfile[profileKey];
- }
-};
-
-this.MigrationUtils = Object.freeze({
- resourceTypes: {
- SETTINGS: Ci.nsIBrowserProfileMigrator.SETTINGS,
- COOKIES: Ci.nsIBrowserProfileMigrator.COOKIES,
- HISTORY: Ci.nsIBrowserProfileMigrator.HISTORY,
- FORMDATA: Ci.nsIBrowserProfileMigrator.FORMDATA,
- PASSWORDS: Ci.nsIBrowserProfileMigrator.PASSWORDS,
- BOOKMARKS: Ci.nsIBrowserProfileMigrator.BOOKMARKS,
- OTHERDATA: Ci.nsIBrowserProfileMigrator.OTHERDATA,
- SESSION: Ci.nsIBrowserProfileMigrator.SESSION,
- },
-
- /**
- * Helper for implementing simple asynchronous cases of migration resources'
- * |migrate(aCallback)| (see MigratorPrototype). If your |migrate| method
- * just waits for some file to be read, for example, and then migrates
- * everything right away, you can wrap the async-function with this helper
- * and not worry about notifying the callback.
- *
- * For example, instead of writing:
- * setTimeout(function() {
- * try {
- * ....
- * aCallback(true);
- * }
- * catch() {
- * aCallback(false);
- * }
- * }, 0);
- *
- * You may write:
- * setTimeout(MigrationUtils.wrapMigrateFunction(function() {
- * if (importingFromMosaic)
- * throw Cr.NS_ERROR_UNEXPECTED;
- * }, aCallback), 0);
- *
- * ... and aCallback will be called with aSuccess=false when importing
- * from Mosaic, or with aSuccess=true otherwise.
- *
- * @param aFunction
- * the function that will be called sometime later. If aFunction
- * throws when it's called, aCallback(false) is called, otherwise
- * aCallback(true) is called.
- * @param aCallback
- * the callback function passed to |migrate|.
- * @return the wrapped function.
- */
- wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) {
- return function() {
- let success = false;
- try {
- aFunction.apply(null, arguments);
- success = true;
- }
- catch (ex) {
- Cu.reportError(ex);
- }
- // Do not change this to call aCallback directly in try try & catch
- // blocks, because if aCallback throws, we may end up calling aCallback
- // twice.
- aCallback(success);
- };
- },
-
- /**
- * Gets a string from the migration bundle. Shorthand for
- * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for
- * nsIStringBundle.formatStringFromName if it is.
- *
- * This method also takes care of "bumped" keys (See bug 737381 comment 8 for
- * details).
- *
- * @param aKey
- * The key of the string to retrieve.
- * @param aReplacements
- * [optioanl] Array of replacements to run on the retrieved string.
- * @return the retrieved string.
- *
- * @see nsIStringBundle
- */
- getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) {
- aKey = aKey.replace(/_(canary|chromium)$/, "_chrome");
-
- const OVERRIDES = {
- "4_firefox": "4_firefox_history_and_bookmarks",
- "64_firefox": "64_firefox_other"
- };
- aKey = OVERRIDES[aKey] || aKey;
-
- if (aReplacements === undefined)
- return getMigrationBundle().GetStringFromName(aKey);
- return getMigrationBundle().formatStringFromName(
- aKey, aReplacements, aReplacements.length);
- },
-
- _getLocalePropertyForBrowser(browserId) {
- switch (browserId) {
- case "edge":
- return "sourceNameEdge";
- case "ie":
- return "sourceNameIE";
- case "safari":
- return "sourceNameSafari";
- case "canary":
- return "sourceNameCanary";
- case "chrome":
- return "sourceNameChrome";
- case "chromium":
- return "sourceNameChromium";
- case "firefox":
- return "sourceNameFirefox";
- case "360se":
- return "sourceName360se";
- }
- return null;
- },
-
- getBrowserName(browserId) {
- let prop = this._getLocalePropertyForBrowser(browserId);
- if (prop) {
- return this.getLocalizedString(prop);
- }
- return null;
- },
-
- /**
- * Helper for creating a folder for imported bookmarks from a particular
- * migration source. The folder is created at the end of the given folder.
- *
- * @param sourceNameStr
- * the source name (first letter capitalized). This is used
- * for reading the localized source name from the migration
- * bundle (e.g. if aSourceNameStr is Mosaic, this will try to read
- * sourceNameMosaic from the migration bundle).
- * @param parentGuid
- * the GUID of the folder in which the new folder should be created.
- * @return the GUID of the new folder.
- */
- createImportedBookmarksFolder: Task.async(function* (sourceNameStr, parentGuid) {
- let source = this.getLocalizedString("sourceName" + sourceNameStr);
- let title = this.getLocalizedString("importedBookmarksFolder", [source]);
- return (yield PlacesUtils.bookmarks.insert({
- type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title
- })).guid;
- }),
-
- /**
- * Get all the rows corresponding to a select query from a database, without
- * requiring a lock on the database. If fetching data fails (because someone
- * else tried to write to the DB at the same time, for example), we will
- * retry the fetch after a 100ms timeout, up to 10 times.
- *
- * @param path
- * the file path to the database we want to open.
- * @param description
- * a developer-readable string identifying what kind of database we're
- * trying to open.
- * @param selectQuery
- * the SELECT query to use to fetch the rows.
- *
- * @return a promise that resolves to an array of rows. The promise will be
- * rejected if the read/fetch failed even after retrying.
- */
- getRowsFromDBWithoutLocks(path, description, selectQuery) {
- let dbOptions = {
- readOnly: true,
- ignoreLockingMode: true,
- path,
- };
-
- const RETRYLIMIT = 10;
- const RETRYINTERVAL = 100;
- return Task.spawn(function* innerGetRows() {
- let rows = null;
- for (let retryCount = RETRYLIMIT; retryCount && !rows; retryCount--) {
- // Attempt to get the rows. If this succeeds, we will bail out of the loop,
- // close the database in a failsafe way, and pass the rows back.
- // If fetching the rows throws, we will wait RETRYINTERVAL ms
- // and try again. This will repeat a maximum of RETRYLIMIT times.
- let db;
- let didOpen = false;
- let exceptionSeen;
- try {
- db = yield Sqlite.openConnection(dbOptions);
- didOpen = true;
- rows = yield db.execute(selectQuery);
- } catch (ex) {
- if (!exceptionSeen) {
- Cu.reportError(ex);
- }
- exceptionSeen = ex;
- } finally {
- try {
- if (didOpen) {
- yield db.close();
- }
- } catch (ex) {}
- }
- if (exceptionSeen) {
- yield new Promise(resolve => setTimeout(resolve, RETRYINTERVAL));
- }
- }
- if (!rows) {
- throw new Error("Couldn't get rows from the " + description + " database.");
- }
- return rows;
- });
- },
-
- get _migrators() {
- if (!gMigrators) {
- gMigrators = new Map();
- }
- return gMigrators;
- },
-
- /*
- * Returns the migrator for the given source, if any data is available
- * for this source, or null otherwise.
- *
- * @param aKey internal name of the migration source.
- * Supported values: ie (windows),
- * edge (windows),
- * safari (mac),
- * canary (mac/windows),
- * chrome (mac/windows/linux),
- * chromium (mac/windows/linux),
- * 360se (windows),
- * firefox.
- *
- * If null is returned, either no data can be imported
- * for the given migrator, or aMigratorKey is invalid (e.g. ie on mac,
- * or mosaic everywhere). This method should be used rather than direct
- * getService for future compatibility (see bug 718280).
- *
- * @return profile migrator implementing nsIBrowserProfileMigrator, if it can
- * import any data, null otherwise.
- */
- getMigrator: function MU_getMigrator(aKey) {
- let migrator = null;
- if (this._migrators.has(aKey)) {
- migrator = this._migrators.get(aKey);
- }
- else {
- try {
- migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" +
- aKey].createInstance(Ci.nsIBrowserProfileMigrator);
- }
- catch (ex) { Cu.reportError(ex) }
- this._migrators.set(aKey, migrator);
- }
-
- try {
- return migrator && migrator.sourceExists ? migrator : null;
- } catch (ex) { Cu.reportError(ex); return null }
- },
-
- /**
- * Figure out what is the default browser, and if there is a migrator
- * for it, return that migrator's internal name.
- * For the time being, the "internal name" of a migrator is its contract-id
- * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
- * but it will soon be exposed properly.
- */
- getMigratorKeyForDefaultBrowser() {
- // Canary uses the same description as Chrome so we can't distinguish them.
- const APP_DESC_TO_KEY = {
- "Internet Explorer": "ie",
- "Microsoft Edge": "edge",
- "Safari": "safari",
- "Basilisk": "firefox",
- "Firefox": "firefox",
- "Nightly": "firefox",
- "Google Chrome": "chrome", // Windows, Linux
- "Chrome": "chrome", // OS X
- "Chromium": "chromium", // Windows, OS X
- "Chromium Web Browser": "chromium", // Linux
- "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
- };
-
- let key = "";
- try {
- let browserDesc =
- Cc["@mozilla.org/uriloader/external-protocol-service;1"]
- .getService(Ci.nsIExternalProtocolService)
- .getApplicationDescription("http");
- key = APP_DESC_TO_KEY[browserDesc] || "";
- // Handle devedition, as well as "FirefoxNightly" on OS X.
- if (!key && browserDesc.startsWith("Firefox")) {
- key = "firefox";
- }
- }
- catch (ex) {
- Cu.reportError("Could not detect default browser: " + ex);
- }
-
- // "firefox" is the least useful entry here, and might just be because we've set
- // ourselves as the default (on Windows 7 and below). In that case, check if we
- // have a registry key that tells us where to go:
- if (key == "firefox" && AppConstants.isPlatformAndVersionAtMost("win", "6.2")) {
- // Because we remove the registry key, reading the registry key only works once.
- // We save the value for subsequent calls to avoid hard-to-trace bugs when multiple
- // consumers ask for this key.
- if (gPreviousDefaultBrowserKey) {
- key = gPreviousDefaultBrowserKey;
- } else {
- // We didn't have a saved value, so check the registry.
- const kRegPath = "Software\\Mozilla\\Firefox";
- let oldDefault = WindowsRegistry.readRegKey(
- Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kRegPath, "OldDefaultBrowserCommand");
- if (oldDefault) {
- // Remove the key:
- WindowsRegistry.removeRegKey(
- Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kRegPath, "OldDefaultBrowserCommand");
- try {
- let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
- file.initWithCommandLine(oldDefault);
- key = APP_DESC_TO_KEY[file.getVersionInfoField("FileDescription")] || key;
- // Save the value for future callers.
- gPreviousDefaultBrowserKey = key;
- } catch (ex) {
- Cu.reportError("Could not convert old default browser value to description.");
- }
- }
- }
- }
- return key;
- },
-
- // Whether or not we're in the process of startup migration
- get isStartupMigration() {
- return gProfileStartup != null;
- },
-
- /**
- * In the case of startup migration, this is set to the nsIProfileStartup
- * instance passed to ProfileMigrator's migrate.
- *
- * @see showMigrationWizard
- */
- get profileStartup() {
- return gProfileStartup;
- },
-
- /**
- * Show the migration wizard. On mac, this may just focus the wizard if it's
- * already running, in which case aOpener and aParams are ignored.
- *
- * @param {Window} [aOpener]
- * optional; the window that asks to open the wizard.
- * @param {Array} [aParams]
- * optional arguments for the migration wizard, in the form of an array
- * This is passed as-is for the params argument of
- * nsIWindowWatcher.openWindow. The array elements we expect are, in
- * order:
- * - {Number} migration entry point constant (see below)
- * - {String} source browser identifier
- * - {nsIBrowserProfileMigrator} actual migrator object
- * - {Boolean} whether this is a startup migration
- * - {Boolean} whether to skip the 'source' page
- * - {String} an identifier for the profile to use when migrating
- * NB: If you add new consumers, please add a migration entry point
- * constant below, and specify at least the first element of the array
- * (the migration entry point for purposes of telemetry).
- */
- showMigrationWizard:
- function MU_showMigrationWizard(aOpener, aParams) {
- let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
- if (AppConstants.platform == "macosx" && !this.isStartupMigration) {
- let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
- if (win) {
- win.focus();
- return;
- }
- // On mac, the migration wiazrd should only be modal in the case of
- // startup-migration.
- features = "centerscreen,chrome,resizable=no";
- }
-
- // nsIWindowWatcher doesn't deal with raw arrays, so we convert the input
- let params;
- if (Array.isArray(aParams)) {
- params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
- for (let item of aParams) {
- let comtaminatedVal;
- if (item && item instanceof Ci.nsISupports) {
- comtaminatedVal = item;
- } else {
- switch (typeof item) {
- case "boolean":
- comtaminatedVal = Cc["@mozilla.org/supports-PRBool;1"].
- createInstance(Ci.nsISupportsPRBool);
- comtaminatedVal.data = item;
- break;
- case "number":
- comtaminatedVal = Cc["@mozilla.org/supports-PRUint32;1"].
- createInstance(Ci.nsISupportsPRUint32);
- comtaminatedVal.data = item;
- break;
- case "string":
- comtaminatedVal = Cc["@mozilla.org/supports-cstring;1"].
- createInstance(Ci.nsISupportsCString);
- comtaminatedVal.data = item;
- break;
-
- case "undefined":
- case "object":
- if (!item) {
- comtaminatedVal = null;
- break;
- }
- /* intentionally falling through to error out here for
- non-null/undefined things: */
- default:
- throw new Error("Unexpected parameter type " + (typeof item) + ": " + item);
- }
- }
- params.appendElement(comtaminatedVal, false);
- }
- } else {
- params = aParams;
- }
-
- Services.ww.openWindow(aOpener,
- "chrome://browser/content/migration/migration.xul",
- "_blank",
- features,
- params);
- },
-
- /**
- * Show the migration wizard for startup-migration. This should only be
- * called by ProfileMigrator (see ProfileMigrator.js), which implements
- * nsIProfileMigrator.
- *
- * @param aProfileStartup
- * the nsIProfileStartup instance provided to ProfileMigrator.migrate.
- * @param [optional] aMigratorKey
- * If set, the migration wizard will import from the corresponding
- * migrator, bypassing the source-selection page. Otherwise, the
- * source-selection page will be displayed, either with the default
- * browser selected, if it could be detected and if there is a
- * migrator for it, or with the first option selected as a fallback
- * (The first option is hardcoded to be the most common browser for
- * the OS we run on. See migration.xul).
- * @param [optional] aProfileToMigrate
- * If set, the migration wizard will import from the profile indicated.
- * @throws if aMigratorKey is invalid or if it points to a non-existent
- * source.
- */
- startupMigration:
- function MU_startupMigrator(aProfileStartup, aMigratorKey, aProfileToMigrate) {
- if (!aProfileStartup) {
- throw new Error("an profile-startup instance is required for startup-migration");
- }
- gProfileStartup = aProfileStartup;
-
- let skipSourcePage = false, migrator = null, migratorKey = "";
- if (aMigratorKey) {
- migrator = this.getMigrator(aMigratorKey);
- if (!migrator) {
- // aMigratorKey must point to a valid source, so, if it doesn't
- // cleanup and throw.
- this.finishMigration();
- throw new Error("startMigration was asked to open auto-migrate from " +
- "a non-existent source: " + aMigratorKey);
- }
- migratorKey = aMigratorKey;
- skipSourcePage = true;
- }
- else {
- let defaultBrowserKey = this.getMigratorKeyForDefaultBrowser();
- if (defaultBrowserKey) {
- migrator = this.getMigrator(defaultBrowserKey);
- if (migrator)
- migratorKey = defaultBrowserKey;
- }
- }
-
- if (!migrator) {
- // If there's no migrator set so far, ensure that there is at least one
- // migrator available before opening the wizard.
- // Note that we don't need to check the default browser first, because
- // if that one existed we would have used it in the block above this one.
- if (!gAvailableMigratorKeys.some(key => !!this.getMigrator(key))) {
- // None of the keys produced a usable migrator, so finish up here:
- this.finishMigration();
- return;
- }
- }
-
- let isRefresh = migrator && skipSourcePage &&
- migratorKey == AppConstants.MOZ_APP_NAME;
-
- if (!isRefresh && AutoMigrate.enabled) {
- try {
- AutoMigrate.migrate(aProfileStartup, migratorKey, aProfileToMigrate);
- return;
- } catch (ex) {
- // If automigration failed, continue and show the dialog.
- Cu.reportError(ex);
- }
- }
-
- let migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FIRSTRUN;
- if (isRefresh) {
- migrationEntryPoint = this.MIGRATION_ENTRYPOINT_FXREFRESH;
- }
-
- let params = [
- migrationEntryPoint,
- migratorKey,
- migrator,
- aProfileStartup,
- skipSourcePage,
- aProfileToMigrate,
- ];
- this.showMigrationWizard(null, params);
- },
-
- _importQuantities: {
- bookmarks: 0,
- logins: 0,
- history: 0,
- },
-
- insertBookmarkWrapper(bookmark) {
- this._importQuantities.bookmarks++;
- let insertionPromise = PlacesUtils.bookmarks.insert(bookmark);
- if (!gKeepUndoData) {
- return insertionPromise;
- }
- // If we keep undo data, add a promise handler that stores the undo data once
- // the bookmark has been inserted in the DB, and then returns the bookmark.
- let {parentGuid} = bookmark;
- return insertionPromise.then(bm => {
- let {guid, lastModified, type} = bm;
- gUndoData.get("bookmarks").push({
- parentGuid, guid, lastModified, type
- });
- return bm;
- });
- },
-
- insertVisitsWrapper(places, options) {
- this._importQuantities.history += places.length;
- if (gKeepUndoData) {
- this._updateHistoryUndo(places);
- }
- return PlacesUtils.asyncHistory.updatePlaces(places, options);
- },
-
- insertLoginWrapper(login) {
- this._importQuantities.logins++;
- let insertedLogin = LoginHelper.maybeImportLogin(login);
- // Note that this means that if we import a login that has a newer password
- // than we know about, we will update the login, and an undo of the import
- // will not revert this. This seems preferable over removing the login
- // outright or storing the old password in the undo file.
- if (insertedLogin && gKeepUndoData) {
- let {guid, timePasswordChanged} = insertedLogin;
- gUndoData.get("logins").push({guid, timePasswordChanged});
- }
- },
-
- initializeUndoData() {
- gKeepUndoData = true;
- gUndoData = new Map([["bookmarks", []], ["visits", []], ["logins", []]]);
- },
-
- _postProcessUndoData: Task.async(function*(state) {
- if (!state) {
- return state;
- }
- let bookmarkFolders = state.get("bookmarks").filter(b => b.type == PlacesUtils.bookmarks.TYPE_FOLDER);
-
- let bookmarkFolderData = [];
- let bmPromises = bookmarkFolders.map(({guid}) => {
- // Ignore bookmarks where the promise doesn't resolve (ie that are missing)
- // Also check that the bookmark fetch returns isn't null before adding it.
- return PlacesUtils.bookmarks.fetch(guid).then(bm => bm && bookmarkFolderData.push(bm), () => {});
- });
-
- yield Promise.all(bmPromises);
- let folderLMMap = new Map(bookmarkFolderData.map(b => [b.guid, b.lastModified]));
- for (let bookmark of bookmarkFolders) {
- let lastModified = folderLMMap.get(bookmark.guid);
- // If the bookmark was deleted, the map will be returning null, so check:
- if (lastModified) {
- bookmark.lastModified = lastModified;
- }
- }
- return state;
- }),
-
- stopAndRetrieveUndoData() {
- let undoData = gUndoData;
- gUndoData = null;
- gKeepUndoData = false;
- return this._postProcessUndoData(undoData);
- },
-
- _updateHistoryUndo(places) {
- let visits = gUndoData.get("visits");
- let visitMap = new Map(visits.map(v => [v.url, v]));
- for (let place of places) {
- let visitCount = place.visits.length;
- let first = Math.min.apply(Math, place.visits.map(v => v.visitDate));
- let last = Math.max.apply(Math, place.visits.map(v => v.visitDate));
- let url = place.uri.spec;
- try {
- new URL(url);
- } catch (ex) {
- // This won't save and we won't need to 'undo' it, so ignore this URL.
- continue;
- }
- if (!visitMap.has(url)) {
- visitMap.set(url, {url, visitCount, first, last});
- } else {
- let currentData = visitMap.get(url);
- currentData.visitCount += visitCount;
- currentData.first = Math.min(currentData.first, first);
- currentData.last = Math.max(currentData.last, last);
- }
- }
- gUndoData.set("visits", Array.from(visitMap.values()));
- },
-
- /**
- * Cleans up references to migrators and nsIProfileInstance instances.
- */
- finishMigration: function MU_finishMigration() {
- gMigrators = null;
- gProfileStartup = null;
- gMigrationBundle = null;
- },
-
- gAvailableMigratorKeys,
-
- MIGRATION_ENTRYPOINT_UNKNOWN: 0,
- MIGRATION_ENTRYPOINT_FIRSTRUN: 1,
- MIGRATION_ENTRYPOINT_FXREFRESH: 2,
- MIGRATION_ENTRYPOINT_PLACES: 3,
- MIGRATION_ENTRYPOINT_PASSWORDS: 4,
-
- _sourceNameToIdMapping: {
- "nothing": 1,
- "firefox": 2,
- "edge": 3,
- "ie": 4,
- "chrome": 5,
- "chromium": 6,
- "canary": 7,
- "safari": 8,
- "360se": 9,
- },
- getSourceIdForTelemetry(sourceName) {
- return this._sourceNameToIdMapping[sourceName] || 0;
- },
-});
diff --git a/browser/components/migration/ProfileMigrator.js b/browser/components/migration/ProfileMigrator.js
deleted file mode 100644
index f67823bae..000000000
--- a/browser/components/migration/ProfileMigrator.js
+++ /dev/null
@@ -1,21 +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/. */
-
-"use strict";
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource:///modules/MigrationUtils.jsm");
-
-function ProfileMigrator() {
-}
-
-ProfileMigrator.prototype = {
- migrate: MigrationUtils.startupMigration.bind(MigrationUtils),
- QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIProfileMigrator]),
- classDescription: "Profile Migrator",
- contractID: "@mozilla.org/toolkit/profile-migrator;1",
- classID: Components.ID("6F8BB968-C14F-4D6F-9733-6C6737B35DCE")
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]);
diff --git a/browser/components/migration/SafariProfileMigrator.js b/browser/components/migration/SafariProfileMigrator.js
deleted file mode 100644
index 6a2dbfcb1..000000000
--- a/browser/components/migration/SafariProfileMigrator.js
+++ /dev/null
@@ -1,650 +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/. */
-
-"use strict";
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
-
-XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
- "resource://gre/modules/Downloads.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PropertyListUtils",
- "resource://gre/modules/PropertyListUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
- "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
- "resource://gre/modules/FormHistory.jsm");
-
-function Bookmarks(aBookmarksFile) {
- this._file = aBookmarksFile;
-}
-Bookmarks.prototype = {
- type: MigrationUtils.resourceTypes.BOOKMARKS,
-
- migrate: function B_migrate(aCallback) {
- return Task.spawn(function* () {
- let dict = yield new Promise(resolve =>
- PropertyListUtils.read(this._file, resolve)
- );
- if (!dict)
- throw new Error("Could not read Bookmarks.plist");
- let children = dict.get("Children");
- if (!children)
- throw new Error("Invalid Bookmarks.plist format");
-
- let collection = dict.get("Title") == "com.apple.ReadingList" ?
- this.READING_LIST_COLLECTION : this.ROOT_COLLECTION;
- yield this._migrateCollection(children, collection);
- }.bind(this)).then(() => aCallback(true),
- e => { Cu.reportError(e); aCallback(false) });
- },
-
- // Bookmarks collections in Safari. Constants for migrateCollection.
- ROOT_COLLECTION: 0,
- MENU_COLLECTION: 1,
- TOOLBAR_COLLECTION: 2,
- READING_LIST_COLLECTION: 3,
-
- /**
- * Recursively migrate a Safari collection of bookmarks.
- *
- * @param aEntries
- * the collection's children
- * @param aCollection
- * one of the values above.
- */
- _migrateCollection: Task.async(function* (aEntries, aCollection) {
- // A collection of bookmarks in Safari resembles places roots. In the
- // property list files (Bookmarks.plist, ReadingList.plist) they are
- // stored as regular bookmarks folders, and thus can only be distinguished
- // from by their names and places in the hierarchy.
-
- let entriesFiltered = [];
- if (aCollection == this.ROOT_COLLECTION) {
- for (let entry of aEntries) {
- let type = entry.get("WebBookmarkType");
- if (type == "WebBookmarkTypeList" && entry.has("Children")) {
- let title = entry.get("Title");
- let children = entry.get("Children");
- if (title == "BookmarksBar")
- yield this._migrateCollection(children, this.TOOLBAR_COLLECTION);
- else if (title == "BookmarksMenu")
- yield this._migrateCollection(children, this.MENU_COLLECTION);
- else if (title == "com.apple.ReadingList")
- yield this._migrateCollection(children, this.READING_LIST_COLLECTION);
- else if (entry.get("ShouldOmitFromUI") !== true)
- entriesFiltered.push(entry);
- }
- else if (type == "WebBookmarkTypeLeaf") {
- entriesFiltered.push(entry);
- }
- }
- }
- else {
- entriesFiltered = aEntries;
- }
-
- if (entriesFiltered.length == 0)
- return;
-
- let folderGuid = -1;
- switch (aCollection) {
- case this.ROOT_COLLECTION: {
- // In Safari, it is possible (though quite cumbersome) to move
- // bookmarks to the bookmarks root, which is the parent folder of
- // all bookmarks "collections". That is somewhat in parallel with
- // both the places root and the unfiled-bookmarks root.
- // Because the former is only an implementation detail in our UI,
- // the unfiled root seems to be the best choice.
- folderGuid = PlacesUtils.bookmarks.unfiledGuid;
- break;
- }
- case this.MENU_COLLECTION: {
- folderGuid = PlacesUtils.bookmarks.menuGuid;
- if (!MigrationUtils.isStartupMigration) {
- folderGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Safari", folderGuid);
- }
- break;
- }
- case this.TOOLBAR_COLLECTION: {
- folderGuid = PlacesUtils.bookmarks.toolbarGuid;
- if (!MigrationUtils.isStartupMigration) {
- folderGuid =
- yield MigrationUtils.createImportedBookmarksFolder("Safari", folderGuid);
- }
- break;
- }
- case this.READING_LIST_COLLECTION: {
- // Reading list items are imported as regular bookmarks.
- // They are imported under their own folder, created either under the
- // bookmarks menu (in the case of startup migration).
- folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- parentGuid: PlacesUtils.bookmarks.menuGuid,
- type: PlacesUtils.bookmarks.TYPE_FOLDER,
- title: MigrationUtils.getLocalizedString("importedSafariReadingList"),
- })).guid;
- break;
- }
- default:
- throw new Error("Unexpected value for aCollection!");
- }
- if (folderGuid == -1)
- throw new Error("Invalid folder GUID");
-
- yield this._migrateEntries(entriesFiltered, folderGuid);
- }),
-
- // migrate the given array of safari bookmarks to the given places
- // folder.
- _migrateEntries: Task.async(function* (entries, parentGuid) {
- for (let entry of entries) {
- let type = entry.get("WebBookmarkType");
- if (type == "WebBookmarkTypeList" && entry.has("Children")) {
- let title = entry.get("Title");
- let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
- parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title
- })).guid;
-
- // Empty folders may not have a children array.
- if (entry.has("Children"))
- yield this._migrateEntries(entry.get("Children"), newFolderGuid, false);
- }
- else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
- let title;
- if (entry.has("URIDictionary"))
- title = entry.get("URIDictionary").get("title");
-
- try {
- yield MigrationUtils.insertBookmarkWrapper({
- parentGuid, url: entry.get("URLString"), title
- });
- } catch (ex) {
- Cu.reportError("Invalid Safari bookmark: " + ex);
- }
- }
- }
- })
-};
-
-function History(aHistoryFile) {
- this._file = aHistoryFile;
-}
-History.prototype = {
- type: MigrationUtils.resourceTypes.HISTORY,
-
- // Helper method for converting the visit date property to a PRTime value.
- // The visit date is stored as a string, so it's not read as a Date
- // object by PropertyListUtils.
- _parseCocoaDate: function H___parseCocoaDate(aCocoaDateStr) {
- let asDouble = parseFloat(aCocoaDateStr);
- if (!isNaN(asDouble)) {
- // reference date of NSDate.
- let date = new Date("1 January 2001, GMT");
- date.setMilliseconds(asDouble * 1000);
- return date * 1000;
- }
- return 0;
- },
-
- migrate: function H_migrate(aCallback) {
- PropertyListUtils.read(this._file, function migrateHistory(aDict) {
- try {
- if (!aDict)
- throw new Error("Could not read history property list");
- if (!aDict.has("WebHistoryDates"))
- throw new Error("Unexpected history-property list format");
-
- // Safari's History file contains only top-level urls. It does not
- // distinguish between typed urls and linked urls.
- let transType = PlacesUtils.history.TRANSITION_LINK;
-
- let places = [];
- let entries = aDict.get("WebHistoryDates");
- for (let entry of entries) {
- if (entry.has("lastVisitedDate")) {
- let visitDate = this._parseCocoaDate(entry.get("lastVisitedDate"));
- try {
- places.push({ uri: NetUtil.newURI(entry.get("")),
- title: entry.get("title"),
- visits: [{ transitionType: transType,
- visitDate: visitDate }] });
- }
- catch (ex) {
- // Safari's History file may contain malformed URIs which
- // will be ignored.
- Cu.reportError(ex);
- }
- }
- }
- if (places.length > 0) {
- MigrationUtils.insertVisitsWrapper(places, {
- _success: false,
- handleResult: function() {
- // Importing any entry is considered a successful import.
- this._success = true;
- },
- handleError: function() {},
- handleCompletion: function() {
- aCallback(this._success);
- }
- });
- }
- else {
- aCallback(false);
- }
- }
- catch (ex) {
- Cu.reportError(ex);
- aCallback(false);
- }
- }.bind(this));
- }
-};
-
-/**
- * Safari's preferences property list is independently used for three purposes:
- * (a) importation of preferences
- * (b) importation of search strings
- * (c) retrieving the home page.
- *
- * So, rather than reading it three times, it's cached and managed here.
- */
-function MainPreferencesPropertyList(aPreferencesFile) {
- this._file = aPreferencesFile;
- this._callbacks = [];
-}
-MainPreferencesPropertyList.prototype = {
- /**
- * @see PropertyListUtils.read
- */
- read: function MPPL_read(aCallback) {
- if ("_dict" in this) {
- aCallback(this._dict);
- return;
- }
-
- let alreadyReading = this._callbacks.length > 0;
- this._callbacks.push(aCallback);
- if (!alreadyReading) {
- PropertyListUtils.read(this._file, function readPrefs(aDict) {
- this._dict = aDict;
- for (let callback of this._callbacks) {
- try {
- callback(aDict);
- }
- catch (ex) {
- Cu.reportError(ex);
- }
- }
- this._callbacks.splice(0);
- }.bind(this));
- }
- },
-
- // Workaround for nsIBrowserProfileMigrator.sourceHomePageURL until
- // it's replaced with an async method.
- _readSync: function MPPL__readSync() {
- if ("_dict" in this)
- return this._dict;
-
- let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
- createInstance(Ci.nsIFileInputStream);
- inputStream.init(this._file, -1, -1, 0);
- let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
- createInstance(Ci.nsIBinaryInputStream);
- binaryStream.setInputStream(inputStream);
- let bytes = binaryStream.readByteArray(inputStream.available());
- this._dict = PropertyListUtils._readFromArrayBufferSync(
- new Uint8Array(bytes).buffer);
- return this._dict;
- }
-};
-
-function Preferences(aMainPreferencesPropertyListInstance) {
- this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
-}
-Preferences.prototype = {
- type: MigrationUtils.resourceTypes.SETTINGS,
-
- migrate: function MPR_migrate(aCallback) {
- this._mainPreferencesPropertyList.read(aDict => {
- Task.spawn(function* () {
- if (!aDict)
- throw new Error("Could not read preferences file");
-
- this._dict = aDict;
-
- let invert = webkitVal => !webkitVal;
- this._set("AutoFillPasswords", "signon.rememberSignons");
- this._set("OpenNewTabsInFront", "browser.tabs.loadInBackground", invert);
- this._set("WebKitJavaScriptCanOpenWindowsAutomatically",
- "dom.disable_open_during_load", invert);
-
- // layout.spellcheckDefault is a boolean stored as a number.
- this._set("WebContinuousSpellCheckingEnabled",
- "layout.spellcheckDefault", Number);
-
- // Auto-load images
- // Firefox has an elaborate set of Image preferences. The correlation is:
- // Mode: Safari Firefox
- // Blocked FALSE 2
- // Allowed TRUE 1
- // Allowed, originating site only -- 3
- this._set("WebKitDisplayImagesKey", "permissions.default.image",
- webkitVal => webkitVal ? 1 : 2);
-
- this._migrateFontSettings();
- yield this._migrateDownloadsFolder();
- }.bind(this)).then(() => aCallback(true), ex => {
- Cu.reportError(ex);
- aCallback(false);
- }).catch(Cu.reportError);
- });
- },
-
- /**
- * Attempts to migrates a preference from Safari. Returns whether the preference
- * has been migrated.
- * @param aSafariKey
- * The dictionary key for the preference of Safari.
- * @param aMozPref
- * The gecko/firefox preference to which aSafariKey should be migrated
- * @param [optional] aConvertFunction(aSafariValue)
- * a function that converts the safari-preference value to the
- * appropriate value for aMozPref. If it's not passed, then the
- * Safari value is set as is.
- * If aConvertFunction returns undefined, then aMozPref is not set
- * at all.
- * @return whether or not aMozPref was set.
- */
- _set: function MPR_set(aSafariKey, aMozPref, aConvertFunction) {
- if (this._dict.has(aSafariKey)) {
- let safariVal = this._dict.get(aSafariKey);
- let mozVal = aConvertFunction !== undefined ?
- aConvertFunction(safariVal) : safariVal;
- switch (typeof mozVal) {
- case "string":
- Services.prefs.setCharPref(aMozPref, mozVal);
- break;
- case "number":
- Services.prefs.setIntPref(aMozPref, mozVal);
- break;
- case "boolean":
- Services.prefs.setBoolPref(aMozPref, mozVal);
- break;
- case "undefined":
- return false;
- default:
- throw new Error("Unexpected value type: " + (typeof mozVal));
- }
- }
- return true;
- },
-
- // Fonts settings are quite problematic for migration, for a couple of
- // reasons:
- // (a) Every font preference in Gecko is set for a particular language.
- // In Safari, each font preference applies to all languages.
- // (b) The current underlying implementation of nsIFontEnumerator cannot
- // really tell you anything about a font: no matter what language or type
- // you try to enumerate with EnumerateFonts, you get an array of all
- // fonts in the systems (This also breaks our fonts dialog).
- // (c) In Gecko, each langauge has a distinct serif and sans-serif font
- // preference. Safari has only one default font setting. It seems that
- // it checks if it's a serif or sans serif font, and when a site
- // explicitly asks to use serif/sans-serif font, it uses the default font
- // only if it applies to this type.
- // (d) The solution of guessing the lang-group out of the default charset (as
- // done in the old Safari migrator) can only work when:
- // (1) The default charset preference is set.
- // (2) It's not a unicode charset.
- // For now, we use the language implied by the system locale as the
- // lang-group. The only exception is minimal font size, which is an
- // accessibility preference in Safari (under the Advanced tab). If it is set,
- // we set it for all languages.
- // As for the font type of the default font (serif/sans-serif), the default
- // type for the given language is used (set in font.default.LANGGROUP).
- _migrateFontSettings: function MPR__migrateFontSettings() {
- // If "Never use font sizes smaller than [ ] is set", migrate it for all
- // languages.
- if (this._dict.has("WebKitMinimumFontSize")) {
- let minimumSize = this._dict.get("WebKitMinimumFontSize");
- if (typeof minimumSize == "number") {
- let prefs = Services.prefs.getChildList("font.minimum-size");
- for (let pref of prefs) {
- Services.prefs.setIntPref(pref, minimumSize);
- }
- }
- else {
- Cu.reportError("WebKitMinimumFontSize was set to an invalid value: " +
- minimumSize);
- }
- }
-
- // In theory, the lang group could be "x-unicode". This will result
- // in setting the fonts for "Other Languages".
- let lang = this._getLocaleLangGroup();
-
- let anySet = false;
- let fontType = Services.prefs.getCharPref("font.default." + lang);
- anySet |= this._set("WebKitFixedFont", "font.name.monospace." + lang);
- anySet |= this._set("WebKitDefaultFixedFontSize", "font.size.fixed." + lang);
- anySet |= this._set("WebKitStandardFont",
- "font.name." + fontType + "." + lang);
- anySet |= this._set("WebKitDefaultFontSize", "font.size.variable." + lang);
-
- // If we set font settings for a particular language, we'll also set the
- // fonts dialog to open with the fonts settings for that langauge.
- if (anySet)
- Services.prefs.setCharPref("font.language.group", lang);
- },
-
- // Get the language group for the system locale.
- _getLocaleLangGroup: function MPR__getLocaleLangGroup() {
- let locale = Services.locale.getLocaleComponentForUserAgent();
-
- // See nsLanguageAtomService::GetLanguageGroup
- let localeLangGroup = "x-unicode";
- let bundle = Services.strings.createBundle(
- "resource://gre/res/langGroups.properties");
- try {
- localeLangGroup = bundle.GetStringFromName(locale);
- }
- catch (ex) {
- let hyphenAt = locale.indexOf("-");
- if (hyphenAt != -1) {
- try {
- localeLangGroup = bundle.GetStringFromName(locale.substr(0, hyphenAt));
- }
- catch (ex2) { }
- }
- }
- return localeLangGroup;
- },
-
- _migrateDownloadsFolder: Task.async(function* () {
- if (!this._dict.has("DownloadsPath"))
- return;
-
- let downloadsFolder = FileUtils.File(this._dict.get("DownloadsPath"));
-
- // If the download folder is set to the Desktop or to ~/Downloads, set the
- // folderList pref appropriately so that "Desktop"/Downloads is shown with
- // pretty name in the preferences dialog.
- let folderListVal = 2;
- if (downloadsFolder.equals(FileUtils.getDir("Desk", []))) {
- folderListVal = 0;
- }
- else {
- let systemDownloadsPath = yield Downloads.getSystemDownloadsDirectory();
- let systemDownloadsFolder = FileUtils.File(systemDownloadsPath);
- if (downloadsFolder.equals(systemDownloadsFolder))
- folderListVal = 1;
- }
- Services.prefs.setIntPref("browser.download.folderList", folderListVal);
- Services.prefs.setComplexValue("browser.download.dir", Ci.nsILocalFile,
- downloadsFolder);
- }),
-};
-
-function SearchStrings(aMainPreferencesPropertyListInstance) {
- this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
-}
-SearchStrings.prototype = {
- type: MigrationUtils.resourceTypes.OTHERDATA,
-
- migrate: function SS_migrate(aCallback) {
- this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
- function migrateSearchStrings(aDict) {
- if (!aDict)
- throw new Error("Could not get preferences dictionary");
-
- if (aDict.has("RecentSearchStrings")) {
- let recentSearchStrings = aDict.get("RecentSearchStrings");
- if (recentSearchStrings && recentSearchStrings.length > 0) {
- let changes = recentSearchStrings.map((searchString) => (
- {op: "add",
- fieldname: "searchbar-history",
- value: searchString}));
- FormHistory.update(changes);
- }
- }
- }.bind(this), aCallback));
- }
-};
-
-// On OS X, the cookie-accept policy preference is stored in a separate
-// property list.
-function WebFoundationCookieBehavior(aWebFoundationFile) {
- this._file = aWebFoundationFile;
-}
-WebFoundationCookieBehavior.prototype = {
- type: MigrationUtils.resourceTypes.SETTINGS,
-
- migrate: function WFPL_migrate(aCallback) {
- PropertyListUtils.read(this._file, MigrationUtils.wrapMigrateFunction(
- function migrateCookieBehavior(aDict) {
- if (!aDict)
- throw new Error("Could not read com.apple.WebFoundation.plist");
-
- if (aDict.has("NSHTTPAcceptCookies")) {
- // Setting Safari Firefox
- // Always Accept always 0
- // Accept from Originating current page 1
- // Never Accept never 2
- let acceptCookies = aDict.get("NSHTTPAcceptCookies");
- let cookieValue = 0;
- if (acceptCookies == "never") {
- cookieValue = 2;
- } else if (acceptCookies == "current page") {
- cookieValue = 1;
- }
- Services.prefs.setIntPref("network.cookie.cookieBehavior",
- cookieValue);
- }
- }.bind(this), aCallback));
- }
-};
-
-function SafariProfileMigrator() {
-}
-
-SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
-
-SafariProfileMigrator.prototype.getResources = function SM_getResources() {
- let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
- if (!profileDir.exists())
- return null;
-
- let resources = [];
- let pushProfileFileResource = function(aFileName, aConstructor) {
- let file = profileDir.clone();
- file.append(aFileName);
- if (file.exists())
- resources.push(new aConstructor(file));
- };
-
- pushProfileFileResource("History.plist", History);
- pushProfileFileResource("Bookmarks.plist", Bookmarks);
-
- // The Reading List feature was introduced at the same time in Windows and
- // Mac versions of Safari. Not surprisingly, they are stored in the same
- // format in both versions. Surpsingly, only on Windows there is a
- // separate property list for it. This code is used on mac too, because
- // Apple may fix this at some point.
- pushProfileFileResource("ReadingList.plist", Bookmarks);
-
- let prefs = this.mainPreferencesPropertyList;
- if (prefs) {
- resources.push(new Preferences(prefs));
- resources.push(new SearchStrings(prefs));
- }
-
- let wfFile = FileUtils.getFile("UsrPrfs", ["com.apple.WebFoundation.plist"]);
- if (wfFile.exists())
- resources.push(new WebFoundationCookieBehavior(wfFile));
-
- return resources;
-};
-
-SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate() {
- let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
- let datePromises = ["Bookmarks.plist", "History.plist"].map(file => {
- let path = OS.Path.join(profileDir.path, file);
- return OS.File.stat(path).catch(() => null).then(info => {
- return info ? info.lastModificationDate : 0;
- });
- });
- return Promise.all(datePromises).then(dates => {
- return new Date(Math.max.apply(Math, dates));
- });
-};
-
-Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", {
- get: function get_mainPreferencesPropertyList() {
- if (this._mainPreferencesPropertyList === undefined) {
- let file = FileUtils.getDir("UsrPrfs", [], false);
- if (file.exists()) {
- file.append("com.apple.Safari.plist");
- if (file.exists()) {
- this._mainPreferencesPropertyList =
- new MainPreferencesPropertyList(file);
- return this._mainPreferencesPropertyList;
- }
- }
- this._mainPreferencesPropertyList = null;
- return this._mainPreferencesPropertyList;
- }
- return this._mainPreferencesPropertyList;
- }
-});
-
-Object.defineProperty(SafariProfileMigrator.prototype, "sourceHomePageURL", {
- get: function get_sourceHomePageURL() {
- if (this.mainPreferencesPropertyList) {
- let dict = this.mainPreferencesPropertyList._readSync();
- if (dict.has("HomePage"))
- return dict.get("HomePage");
- }
- return "";
- }
-});
-
-SafariProfileMigrator.prototype.classDescription = "Safari Profile Migrator";
-SafariProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=safari";
-SafariProfileMigrator.prototype.classID = Components.ID("{4b609ecf-60b2-4655-9df4-dc149e474da1}");
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SafariProfileMigrator]);
diff --git a/browser/components/migration/content/aboutWelcomeBack.xhtml b/browser/components/migration/content/aboutWelcomeBack.xhtml
deleted file mode 100644
index d9fdb6c2c..000000000
--- a/browser/components/migration/content/aboutWelcomeBack.xhtml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-# 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/.
--->
-<!DOCTYPE html [
- <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
- %htmlDTD;
- <!ENTITY % netErrorDTD SYSTEM "chrome://global/locale/netError.dtd">
- %netErrorDTD;
- <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
- %globalDTD;
- <!ENTITY % restorepageDTD SYSTEM "chrome://browser/locale/aboutSessionRestore.dtd">
- %restorepageDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <head>
- <title>&welcomeback2.tabtitle;</title>
- <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css" media="all"/>
- <link rel="stylesheet" href="chrome://browser/skin/aboutWelcomeBack.css" type="text/css" media="all"/>
- <link rel="icon" type="image/png" href="chrome://global/skin/icons/information-16.png"/>
-
- <script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSessionRestore.js"/>
- </head>
-
- <body dir="&locale.dir;">
-
- <div class="container">
-
- <div class="title">
- <h1 class="title-text">&welcomeback2.pageTitle;</h1>
- </div>
-
- <div class="description">
-
- <p>&welcomeback2.pageInfo1;</p>
- <!-- Note a href in the anchor below is added by JS -->
- <p>&welcomeback2.beforelink.pageInfo2;<a id="linkMoreTroubleshooting" target="_blank">&welcomeback2.link.pageInfo2;</a>&welcomeback2.afterlink.pageInfo2;</p>
-
- <div>
- <div class="radioRestoreContainer">
- <input class="radioRestoreButton" id="radioRestoreAll" type="radio"
- name="restore" checked="checked"/>
- <label class="radioRestoreLabel" for="radioRestoreAll">&welcomeback2.label.restoreAll;</label>
- </div>
-
- <div class="radioRestoreContainer">
- <input class="radioRestoreButton" id="radioRestoreChoose" type="radio"
- name="restore"/>
- <label class="radioRestoreLabel" for="radioRestoreChoose">&welcomeback2.label.restoreSome;</label>
- </div>
- </div>
- </div>
-
- <div class="tree-container">
- <xul:tree id="tabList" flex="1" seltype="single" hidecolumnpicker="true"
- onclick="onListClick(event);" onkeydown="onListKeyDown(event);"
- _window_label="&restorepage.windowLabel;">
- <xul:treecols>
- <xul:treecol cycler="true" id="restore" type="checkbox" label="&restorepage.restoreHeader;"/>
- <xul:splitter class="tree-splitter"/>
- <xul:treecol primary="true" id="title" label="&restorepage.listHeader;" flex="1"/>
- </xul:treecols>
- <xul:treechildren flex="1"/>
- </xul:tree>
- </div>
-
- <div class="button-container">
- <xul:button class="primary"
- id="errorTryAgain"
- label="&welcomeback2.restoreButton;"
- accesskey="&welcomeback2.restoreButton.access;"
- oncommand="restoreSession();"/>
- </div>
-
- <input type="text" id="sessionData" style="display: none;"/>
-
- </div>
- </body>
-</html>
diff --git a/browser/components/migration/content/extra-migration-strings.properties b/browser/components/migration/content/extra-migration-strings.properties
deleted file mode 100644
index 208906b31..000000000
--- a/browser/components/migration/content/extra-migration-strings.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# Automigration undo notification.
-# %1$S will be replaced with the name of the browser we imported from, %2$S will be replaced with brandShortName
-automigration.undo.message.all = Pick up where you left off. We’ve imported these sites and your bookmarks, history and passwords from %1$S into %2$S.
-automigration.undo.message.bookmarks = Pick up where you left off. We’ve imported these sites and your bookmarks from %1$S into %2$S.
-automigration.undo.message.bookmarks.logins = Pick up where you left off. We’ve imported these sites and your bookmarks and passwords from %1$S into %2$S.
-automigration.undo.message.bookmarks.visits = Pick up where you left off. We’ve imported these sites and your bookmarks and history from %1$S into %2$S.
-automigration.undo.message.logins = Pick up where you left off. We’ve imported your passwords from %1$S into %2$S.
-automigration.undo.message.logins.visits = Pick up where you left off. We’ve imported these sites and your history and passwords from %1$S into %2$S.
-automigration.undo.message.visits = Pick up where you left off. We’ve imported these sites and your history from %1$S into %2$S.
-automigration.undo.keep2.label = OK, Got it
-automigration.undo.keep2.accesskey = O
-automigration.undo.dontkeep2.label = No Thanks
-automigration.undo.dontkeep2.accesskey = N
-automigration.undo.unknownbrowser = Unknown Browser
diff --git a/browser/components/migration/content/migration.js b/browser/components/migration/content/migration.js
deleted file mode 100644
index eb2175628..000000000
--- a/browser/components/migration/content/migration.js
+++ /dev/null
@@ -1,549 +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/. */
-
-"use strict";
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-const kIMig = Ci.nsIBrowserProfileMigrator;
-const kIPStartup = Ci.nsIProfileStartup;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/MigrationUtils.jsm");
-
-var MigrationWizard = { /* exported MigrationWizard */
- _source: "", // Source Profile Migrator ContractID suffix
- _itemsFlags: kIMig.ALL, // Selected Import Data Sources (16-bit bitfield)
- _selectedProfile: null, // Selected Profile name to import from
- _wiz: null,
- _migrator: null,
- _autoMigrate: null,
-
- init: function ()
- {
- let os = Services.obs;
- os.addObserver(this, "Migration:Started", false);
- os.addObserver(this, "Migration:ItemBeforeMigrate", false);
- os.addObserver(this, "Migration:ItemAfterMigrate", false);
- os.addObserver(this, "Migration:ItemError", false);
- os.addObserver(this, "Migration:Ended", false);
-
- this._wiz = document.documentElement;
-
- let args = window.arguments;
- let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
- Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);
- this.isInitialMigration = entryPointId == MigrationUtils.MIGRATION_ENTRYPOINT_FIRSTRUN;
-
- if (args.length > 1) {
- this._source = args[1];
- this._migrator = args[2] instanceof kIMig ? args[2] : null;
- this._autoMigrate = args[3].QueryInterface(kIPStartup);
- this._skipImportSourcePage = args[4];
- if (this._migrator && args[5]) {
- let sourceProfiles = this._migrator.sourceProfiles;
- this._selectedProfile = sourceProfiles.find(profile => profile.id == args[5]);
- }
-
- if (this._autoMigrate) {
- // Show the "nothing" option in the automigrate case to provide an
- // easily identifiable way to avoid migration and create a new profile.
- document.getElementById("nothing").hidden = false;
- }
- }
-
- this.onImportSourcePageShow();
- },
-
- uninit: function ()
- {
- var os = Components.classes["@mozilla.org/observer-service;1"]
- .getService(Components.interfaces.nsIObserverService);
- os.removeObserver(this, "Migration:Started");
- os.removeObserver(this, "Migration:ItemBeforeMigrate");
- os.removeObserver(this, "Migration:ItemAfterMigrate");
- os.removeObserver(this, "Migration:ItemError");
- os.removeObserver(this, "Migration:Ended");
- MigrationUtils.finishMigration();
- },
-
- // 1 - Import Source
- onImportSourcePageShow: function ()
- {
- // Show warning message to close the selected browser when needed
- function toggleCloseBrowserWarning() {
- let visibility = "hidden";
- if (group.selectedItem.id != "nothing") {
- let migrator = MigrationUtils.getMigrator(group.selectedItem.id);
- visibility = migrator.sourceLocked ? "visible" : "hidden";
- }
- document.getElementById("closeSourceBrowser").style.visibility = visibility;
- }
- this._wiz.canRewind = false;
-
- var selectedMigrator = null;
- this._availableMigrators = [];
-
- // Figure out what source apps are are available to import from:
- var group = document.getElementById("importSourceGroup");
- for (var i = 0; i < group.childNodes.length; ++i) {
- var migratorKey = group.childNodes[i].id;
- if (migratorKey != "nothing") {
- var migrator = MigrationUtils.getMigrator(migratorKey);
- if (migrator) {
- // Save this as the first selectable item, if we don't already have
- // one, or if it is the migrator that was passed to us.
- if (!selectedMigrator || this._source == migratorKey)
- selectedMigrator = group.childNodes[i];
- this._availableMigrators.push([migratorKey, migrator]);
- } else {
- // Hide this option
- group.childNodes[i].hidden = true;
- }
- }
- }
- if (this.isInitialMigration) {
- Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT")
- .add(this._availableMigrators.length);
- let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser();
- // This will record 0 for unknown default browser IDs.
- defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser);
- Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_EXISTING_DEFAULT_BROWSER")
- .add(defaultBrowser);
- }
-
- group.addEventListener("command", toggleCloseBrowserWarning);
-
- if (selectedMigrator) {
- group.selectedItem = selectedMigrator;
- toggleCloseBrowserWarning();
- } else {
- // We didn't find a migrator, notify the user
- document.getElementById("noSources").hidden = false;
-
- this._wiz.canAdvance = false;
-
- document.getElementById("importBookmarks").hidden = true;
- document.getElementById("importAll").hidden = true;
- }
-
- // Advance to the next page if the caller told us to.
- if (this._migrator && this._skipImportSourcePage) {
- this._wiz.advance();
- this._wiz.canRewind = false;
- }
- },
-
- onImportSourcePageAdvanced: function ()
- {
- var newSource = document.getElementById("importSourceGroup").selectedItem.id;
-
- if (newSource == "nothing") {
- // Need to do telemetry here because we're closing the dialog before we get to
- // do actual migration. For actual migration, this doesn't happen until after
- // migration takes place.
- Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
- .add(MigrationUtils.getSourceIdForTelemetry("nothing"));
- document.documentElement.cancel();
- return false;
- }
-
- if (!this._migrator || (newSource != this._source)) {
- // Create the migrator for the selected source.
- this._migrator = MigrationUtils.getMigrator(newSource);
-
- this._itemsFlags = kIMig.ALL;
- this._selectedProfile = null;
- }
- this._source = newSource;
-
- // check for more than one source profile
- var sourceProfiles = this._migrator.sourceProfiles;
- if (this._skipImportSourcePage) {
- this._wiz.currentPage.next = "homePageImport";
- }
- else if (sourceProfiles && sourceProfiles.length > 1) {
- this._wiz.currentPage.next = "selectProfile";
- }
- else {
- if (this._autoMigrate)
- this._wiz.currentPage.next = "homePageImport";
- else
- this._wiz.currentPage.next = "importItems";
-
- if (sourceProfiles && sourceProfiles.length == 1)
- this._selectedProfile = sourceProfiles[0];
- else
- this._selectedProfile = null;
- }
- return undefined;
- },
-
- // 2 - [Profile Selection]
- onSelectProfilePageShow: function ()
- {
- // Disabling this for now, since we ask about import sources in automigration
- // too and don't want to disable the back button
- // if (this._autoMigrate)
- // document.documentElement.getButton("back").disabled = true;
-
- var profiles = document.getElementById("profiles");
- while (profiles.hasChildNodes())
- profiles.removeChild(profiles.firstChild);
-
- // Note that this block is still reached even if the user chose 'From File'
- // and we canceled the dialog. When that happens, _migrator will be null.
- if (this._migrator) {
- var sourceProfiles = this._migrator.sourceProfiles;
-
- for (let profile of sourceProfiles) {
- var item = document.createElement("radio");
- item.id = profile.id;
- item.setAttribute("label", profile.name);
- profiles.appendChild(item);
- }
- }
-
- profiles.selectedItem = this._selectedProfile ? document.getElementById(this._selectedProfile.id) : profiles.firstChild;
- },
-
- onSelectProfilePageRewound: function ()
- {
- var profiles = document.getElementById("profiles");
- this._selectedProfile = this._migrator.sourceProfiles.find(
- profile => profile.id == profiles.selectedItem.id
- ) || null;
- },
-
- onSelectProfilePageAdvanced: function ()
- {
- var profiles = document.getElementById("profiles");
- this._selectedProfile = this._migrator.sourceProfiles.find(
- profile => profile.id == profiles.selectedItem.id
- ) || null;
-
- // If we're automigrating or just doing bookmarks don't show the item selection page
- if (this._autoMigrate)
- this._wiz.currentPage.next = "homePageImport";
- },
-
- // 3 - ImportItems
- onImportItemsPageShow: function ()
- {
- var dataSources = document.getElementById("dataSources");
- while (dataSources.hasChildNodes())
- dataSources.removeChild(dataSources.firstChild);
-
- var items = this._migrator.getMigrateData(this._selectedProfile, this._autoMigrate);
- for (var i = 0; i < 16; ++i) {
- var itemID = (items >> i) & 0x1 ? Math.pow(2, i) : 0;
- if (itemID > 0) {
- var checkbox = document.createElement("checkbox");
- checkbox.id = itemID;
- checkbox.setAttribute("label",
- MigrationUtils.getLocalizedString(itemID + "_" + this._source));
- dataSources.appendChild(checkbox);
- if (!this._itemsFlags || this._itemsFlags & itemID)
- checkbox.checked = true;
- }
- }
- },
-
- onImportItemsPageRewound: function ()
- {
- this._wiz.canAdvance = true;
- this.onImportItemsPageAdvanced();
- },
-
- onImportItemsPageAdvanced: function ()
- {
- var dataSources = document.getElementById("dataSources");
- this._itemsFlags = 0;
- for (var i = 0; i < dataSources.childNodes.length; ++i) {
- var checkbox = dataSources.childNodes[i];
- if (checkbox.localName == "checkbox" && checkbox.checked)
- this._itemsFlags |= parseInt(checkbox.id);
- }
- },
-
- onImportItemCommand: function ()
- {
- var items = document.getElementById("dataSources");
- var checkboxes = items.getElementsByTagName("checkbox");
-
- var oneChecked = false;
- for (var i = 0; i < checkboxes.length; ++i) {
- if (checkboxes[i].checked) {
- oneChecked = true;
- break;
- }
- }
-
- this._wiz.canAdvance = oneChecked;
- },
-
- // 4 - Home Page Selection
- onHomePageMigrationPageShow: function ()
- {
- // only want this on the first run
- if (!this._autoMigrate) {
- this._wiz.advance();
- return;
- }
-
- var brandBundle = document.getElementById("brandBundle");
- var pageTitle, pageDesc, mainStr;
- // These strings don't exist when not using official branding. If that's
- // the case, just skip this page.
- try {
- pageTitle = brandBundle.getString("homePageMigrationPageTitle");
- pageDesc = brandBundle.getString("homePageMigrationDescription");
- mainStr = brandBundle.getString("homePageSingleStartMain");
- }
- catch (e) {
- this._wiz.advance();
- return;
- }
-
- document.getElementById("homePageImport").setAttribute("label", pageTitle);
- document.getElementById("homePageImportDesc").setAttribute("value", pageDesc);
-
- this._wiz._adjustWizardHeader();
-
- var singleStart = document.getElementById("homePageSingleStart");
- singleStart.setAttribute("label", mainStr);
- singleStart.setAttribute("value", "DEFAULT");
-
- var appName = MigrationUtils.getBrowserName(this._source);
-
- // semi-wallpaper for crash when multiple profiles exist, since we haven't initialized mSourceProfile in places
- this._migrator.getMigrateData(this._selectedProfile, this._autoMigrate);
-
- var oldHomePageURL = this._migrator.sourceHomePageURL;
-
- if (oldHomePageURL && appName) {
- var oldHomePageLabel =
- brandBundle.getFormattedString("homePageImport", [appName]);
- var oldHomePage = document.getElementById("oldHomePage");
- oldHomePage.setAttribute("label", oldHomePageLabel);
- oldHomePage.setAttribute("value", oldHomePageURL);
- oldHomePage.removeAttribute("hidden");
- }
- else {
- // if we don't have at least two options, just advance
- this._wiz.advance();
- }
- },
-
- onHomePageMigrationPageAdvanced: function ()
- {
- // we might not have a selectedItem if we're in fallback mode
- try {
- var radioGroup = document.getElementById("homePageRadiogroup");
-
- this._newHomePage = radioGroup.selectedItem.value;
- } catch (ex) {}
- },
-
- // 5 - Migrating
- onMigratingPageShow: function ()
- {
- this._wiz.getButton("cancel").disabled = true;
- this._wiz.canRewind = false;
- this._wiz.canAdvance = false;
-
- // When automigrating, show all of the data that can be received from this source.
- if (this._autoMigrate)
- this._itemsFlags = this._migrator.getMigrateData(this._selectedProfile, this._autoMigrate);
-
- this._listItems("migratingItems");
- setTimeout(() => this.onMigratingMigrate(), 0);
- },
-
- onMigratingMigrate: function ()
- {
- this._migrator.migrate(this._itemsFlags, this._autoMigrate, this._selectedProfile);
-
- Services.telemetry.getHistogramById("FX_MIGRATION_SOURCE_BROWSER")
- .add(MigrationUtils.getSourceIdForTelemetry(this._source));
- if (!this._autoMigrate) {
- let hist = Services.telemetry.getKeyedHistogramById("FX_MIGRATION_USAGE");
- let exp = 0;
- let items = this._itemsFlags;
- while (items) {
- if (items & 1) {
- hist.add(this._source, exp);
- }
- items = items >> 1;
- exp++;
- }
- }
- },
-
- _listItems: function (aID)
- {
- var items = document.getElementById(aID);
- while (items.hasChildNodes())
- items.removeChild(items.firstChild);
-
- var itemID;
- for (var i = 0; i < 16; ++i) {
- itemID = (this._itemsFlags >> i) & 0x1 ? Math.pow(2, i) : 0;
- if (itemID > 0) {
- var label = document.createElement("label");
- label.id = itemID + "_migrated";
- try {
- label.setAttribute("value",
- MigrationUtils.getLocalizedString(itemID + "_" + this._source));
- items.appendChild(label);
- }
- catch (e) {
- // if the block above throws, we've enumerated all the import data types we
- // currently support and are now just wasting time, break.
- break;
- }
- }
- }
- },
-
- observe: function (aSubject, aTopic, aData)
- {
- var label;
- switch (aTopic) {
- case "Migration:Started":
- break;
- case "Migration:ItemBeforeMigrate":
- label = document.getElementById(aData + "_migrated");
- if (label)
- label.setAttribute("style", "font-weight: bold");
- break;
- case "Migration:ItemAfterMigrate":
- label = document.getElementById(aData + "_migrated");
- if (label)
- label.removeAttribute("style");
- break;
- case "Migration:Ended":
- if (this.isInitialMigration) {
- // Ensure errors in reporting data recency do not affect the rest of the migration.
- try {
- this.reportDataRecencyTelemetry();
- } catch (ex) {
- Cu.reportError(ex);
- }
- }
- if (this._autoMigrate) {
- let hasImportedHomepage = !!(this._newHomePage && this._newHomePage != "DEFAULT");
- Services.telemetry.getKeyedHistogramById("FX_MIGRATION_IMPORTED_HOMEPAGE")
- .add(this._source, hasImportedHomepage);
- if (this._newHomePage) {
- try {
- // set homepage properly
- var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefService);
- var prefBranch = prefSvc.getBranch(null);
-
- if (this._newHomePage == "DEFAULT") {
- prefBranch.clearUserPref("browser.startup.homepage");
- }
- else {
- var str = Components.classes["@mozilla.org/supports-string;1"]
- .createInstance(Components.interfaces.nsISupportsString);
- str.data = this._newHomePage;
- prefBranch.setComplexValue("browser.startup.homepage",
- Components.interfaces.nsISupportsString,
- str);
- }
-
- var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
- .getService(Components.interfaces.nsIProperties);
- var prefFile = dirSvc.get("ProfDS", Components.interfaces.nsIFile);
- prefFile.append("prefs.js");
- prefSvc.savePrefFile(prefFile);
- } catch (ex) {
- dump(ex);
- }
- }
-
- // We're done now.
- this._wiz.canAdvance = true;
- this._wiz.advance();
-
- setTimeout(close, 5000);
- }
- else {
- this._wiz.canAdvance = true;
- var nextButton = this._wiz.getButton("next");
- nextButton.click();
- }
- break;
- case "Migration:ItemError":
- let type = "undefined";
- let numericType = parseInt(aData);
- switch (numericType) {
- case Ci.nsIBrowserProfileMigrator.SETTINGS:
- type = "settings";
- break;
- case Ci.nsIBrowserProfileMigrator.COOKIES:
- type = "cookies";
- break;
- case Ci.nsIBrowserProfileMigrator.HISTORY:
- type = "history";
- break;
- case Ci.nsIBrowserProfileMigrator.FORMDATA:
- type = "form data";
- break;
- case Ci.nsIBrowserProfileMigrator.PASSWORDS:
- type = "passwords";
- break;
- case Ci.nsIBrowserProfileMigrator.BOOKMARKS:
- type = "bookmarks";
- break;
- case Ci.nsIBrowserProfileMigrator.OTHERDATA:
- type = "misc. data";
- break;
- }
- Cc["@mozilla.org/consoleservice;1"]
- .getService(Ci.nsIConsoleService)
- .logStringMessage("some " + type + " did not successfully migrate.");
- Services.telemetry.getKeyedHistogramById("FX_MIGRATION_ERRORS")
- .add(this._source, Math.log2(numericType));
- break;
- }
- },
-
- onDonePageShow: function ()
- {
- this._wiz.getButton("cancel").disabled = true;
- this._wiz.canRewind = false;
- this._listItems("doneItems");
- },
-
- reportDataRecencyTelemetry() {
- let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_DATA_RECENCY");
- let lastUsedPromises = [];
- for (let [key, migrator] of this._availableMigrators) {
- // No block-scoped let in for...of loop conditions, so get the source:
- let localKey = key;
- lastUsedPromises.push(migrator.getLastUsedDate().then(date => {
- const ONE_YEAR = 24 * 365;
- let diffInHours = Math.round((Date.now() - date) / (60 * 60 * 1000));
- if (diffInHours > ONE_YEAR) {
- diffInHours = ONE_YEAR;
- }
- histogram.add(localKey, diffInHours);
- return [localKey, diffInHours];
- }));
- }
- Promise.all(lastUsedPromises).then(migratorUsedTimeDiff => {
- // Sort low to high.
- migratorUsedTimeDiff.sort(([keyA, diffA], [keyB, diffB]) => diffA - diffB); /* eslint no-unused-vars: off */
- let usedMostRecentBrowser = migratorUsedTimeDiff.length && this._source == migratorUsedTimeDiff[0][0];
- let usedRecentBrowser =
- Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_USED_RECENT_BROWSER");
- usedRecentBrowser.add(this._source, usedMostRecentBrowser);
- });
- },
-};
diff --git a/browser/components/migration/content/migration.xul b/browser/components/migration/content/migration.xul
deleted file mode 100644
index e85091002..000000000
--- a/browser/components/migration/content/migration.xul
+++ /dev/null
@@ -1,109 +0,0 @@
-<?xml version="1.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/.
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-
-<!DOCTYPE dialog SYSTEM "chrome://browser/locale/migration/migration.dtd" >
-
-<wizard id="migrationWizard"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- windowtype="Browser:MigrationWizard"
- title="&migrationWizard.title;"
- onload="MigrationWizard.init()"
- onunload="MigrationWizard.uninit()"
- style="width: 40em;"
- buttons="accept,cancel"
- branded="true">
-
- <script type="application/javascript" src="chrome://browser/content/migration/migration.js"/>
-
- <stringbundle id="brandBundle" src="chrome://branding/locale/brand.properties"/>
-
- <wizardpage id="importSource" pageid="importSource" next="selectProfile"
- label="&importSource.title;"
- onpageadvanced="return MigrationWizard.onImportSourcePageAdvanced();">
-#ifdef XP_WIN
- <description id="importAll" control="importSourceGroup">&importFrom.label;</description>
-#else
- <description id="importAll" control="importSourceGroup">&importFromUnix.label;</description>
-#endif
- <description id="importBookmarks" control="importSourceGroup" hidden="true">&importFromBookmarks.label;</description>
-
- <radiogroup id="importSourceGroup" align="start">
-# NB: if you add items to this list, please also assign them a unique migrator ID in MigrationUtils.jsm
- <radio id="firefox" label="&importFromFirefox.label;" accesskey="&importFromFirefox.accesskey;"/>
-#ifdef XP_WIN
- <radio id="edge" label="&importFromEdge.label;" accesskey="&importFromEdge.accesskey;"/>
- <radio id="ie" label="&importFromIE.label;" accesskey="&importFromIE.accesskey;"/>
- <radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
- <radio id="chromium" label="&importFromChromium.label;" accesskey="&importFromChromium.accesskey;"/>
- <radio id="canary" label="&importFromCanary.label;" accesskey="&importFromCanary.accesskey;"/>
- <radio id="360se" label="&importFrom360se.label;" accesskey="&importFrom360se.accesskey;"/>
-#elifdef XP_MACOSX
- <radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/>
- <radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
- <radio id="chromium" label="&importFromChromium.label;" accesskey="&importFromChromium.accesskey;"/>
- <radio id="canary" label="&importFromCanary.label;" accesskey="&importFromCanary.accesskey;"/>
-#elifdef XP_UNIX
- <radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
- <radio id="chromium" label="&importFromChromium.label;" accesskey="&importFromChromium.accesskey;"/>
-#endif
- <radio id="nothing" label="&importFromNothing.label;" accesskey="&importFromNothing.accesskey;" hidden="true"/>
- </radiogroup>
- <label id="noSources" hidden="true">&noMigrationSources.label;</label>
- <spacer flex="1"/>
- <description class="header" id="closeSourceBrowser" style="visibility:hidden">&closeSourceBrowser.label;</description>
- </wizardpage>
-
- <wizardpage id="selectProfile" pageid="selectProfile" label="&selectProfile.title;"
- next="importItems"
- onpageshow="return MigrationWizard.onSelectProfilePageShow();"
- onpagerewound="return MigrationWizard.onSelectProfilePageRewound();"
- onpageadvanced="return MigrationWizard.onSelectProfilePageAdvanced();">
- <description control="profiles">&selectProfile.label;</description>
-
- <radiogroup id="profiles" align="left"/>
- </wizardpage>
-
- <wizardpage id="importItems" pageid="importItems" label="&importItems.title;"
- next="homePageImport"
- onpageshow="return MigrationWizard.onImportItemsPageShow();"
- onpagerewound="return MigrationWizard.onImportItemsPageRewound();"
- onpageadvanced="return MigrationWizard.onImportItemsPageAdvanced();"
- oncommand="MigrationWizard.onImportItemCommand();">
- <description control="dataSources">&importItems.label;</description>
-
- <vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="left" flex="1" role="group"/>
- </wizardpage>
-
- <wizardpage id="homePageImport" pageid="homePageImport"
- next="migrating"
- onpageshow="return MigrationWizard.onHomePageMigrationPageShow();"
- onpageadvanced="return MigrationWizard.onHomePageMigrationPageAdvanced();">
-
- <description id="homePageImportDesc" control="homePageRadioGroup"/>
- <radiogroup id="homePageRadiogroup">
- <radio id="homePageSingleStart" selected="true" />
- <radio id="oldHomePage" hidden="true" />
- </radiogroup>
- </wizardpage>
-
- <wizardpage id="migrating" pageid="migrating" label="&migrating.title;"
- next="done"
- onpageshow="MigrationWizard.onMigratingPageShow();">
- <description control="migratingItems">&migrating.label;</description>
-
- <vbox id="migratingItems" style="overflow: auto;" align="left" role="group"/>
- </wizardpage>
-
- <wizardpage id="done" pageid="done" label="&done.title;"
- onpageshow="MigrationWizard.onDonePageShow();">
- <description control="doneItems">&done.label;</description>
-
- <vbox id="doneItems" style="overflow: auto;" align="left" role="group"/>
- </wizardpage>
-
-</wizard>
-
diff --git a/browser/components/migration/jar.mn b/browser/components/migration/jar.mn
deleted file mode 100644
index 110788bc4..000000000
--- a/browser/components/migration/jar.mn
+++ /dev/null
@@ -1,9 +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/.
-
-browser.jar:
-* content/browser/migration/migration.xul (content/migration.xul)
- content/browser/migration/migration.js (content/migration.js)
- content/browser/migration/extra-migration-strings.properties (content/extra-migration-strings.properties)
- content/browser/aboutWelcomeBack.xhtml (content/aboutWelcomeBack.xhtml)
diff --git a/browser/components/migration/moz.build b/browser/components/migration/moz.build
deleted file mode 100644
index 465e3eade..000000000
--- a/browser/components/migration/moz.build
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-XPIDL_SOURCES += [
- 'nsIBrowserProfileMigrator.idl',
-]
-
-XPIDL_MODULE = 'migration'
-
-EXTRA_COMPONENTS += [
- 'ChromeProfileMigrator.js',
- 'FirefoxProfileMigrator.js',
- 'ProfileMigrator.js',
-]
-
-EXTRA_PP_COMPONENTS += [
- 'BrowserProfileMigrators.manifest',
-]
-
-EXTRA_JS_MODULES += [
- 'AutoMigrate.jsm',
- 'MigrationUtils.jsm',
-]
-
-if CONFIG['OS_ARCH'] == 'WINNT':
- SOURCES += [
- 'nsIEHistoryEnumerator.cpp',
- ]
- EXTRA_COMPONENTS += [
- '360seProfileMigrator.js',
- 'EdgeProfileMigrator.js',
- 'IEProfileMigrator.js',
- ]
- EXTRA_JS_MODULES += [
- 'ESEDBReader.jsm',
- 'MSMigrationUtils.jsm',
- ]
- DEFINES['HAS_360SE_MIGRATOR'] = True
- DEFINES['HAS_IE_MIGRATOR'] = True
- DEFINES['HAS_EDGE_MIGRATOR'] = True
-
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
- EXTRA_COMPONENTS += [
- 'SafariProfileMigrator.js',
- ]
- DEFINES['HAS_SAFARI_MIGRATOR'] = True
-
-FINAL_LIBRARY = 'browsercomps'
diff --git a/browser/components/migration/nsIBrowserProfileMigrator.idl b/browser/components/migration/nsIBrowserProfileMigrator.idl
deleted file mode 100644
index a251c3683..000000000
--- a/browser/components/migration/nsIBrowserProfileMigrator.idl
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-#include "nsISupports.idl"
-
-interface nsIArray;
-interface nsIProfileStartup;
-
-[scriptable, uuid(22b56ffc-3149-43c5-b5a9-b3a6b678de93)]
-interface nsIBrowserProfileMigrator : nsISupports
-{
- /**
- * profile items to migrate. use with migrate().
- */
- const unsigned short ALL = 0x0000;
- const unsigned short SETTINGS = 0x0001;
- const unsigned short COOKIES = 0x0002;
- const unsigned short HISTORY = 0x0004;
- const unsigned short FORMDATA = 0x0008;
- const unsigned short PASSWORDS = 0x0010;
- const unsigned short BOOKMARKS = 0x0020;
- const unsigned short OTHERDATA = 0x0040;
- const unsigned short SESSION = 0x0080;
-
- /**
- * Copy user profile information to the current active profile.
- * @param aItems list of data items to migrate. see above for values.
- * @param aStartup helper interface which is non-null if called during startup.
- * @param aProfile profile to migrate from, if there is more than one.
- */
- void migrate(in unsigned short aItems, in nsIProfileStartup aStartup, in jsval aProfile);
-
- /**
- * A bit field containing profile items that this migrator
- * offers for import.
- * @param aProfile the profile that we are looking for available data
- * to import
- * @param aDoingStartup "true" if the profile is not currently being used.
- * @return bit field containing profile items (see above)
- * @note a return value of 0 represents no items rather than ALL.
- */
- unsigned short getMigrateData(in jsval aProfile, in boolean aDoingStartup);
-
- /**
- * Get the last time data from this browser was modified
- * @return a promise that resolves to a JS Date object
- */
- jsval getLastUsedDate();
-
- /**
- * Whether or not there is any data that can be imported from this
- * browser (i.e. whether or not it is installed, and there exists
- * a user profile)
- */
- readonly attribute boolean sourceExists;
-
-
- /**
- * An enumeration of available profiles. If the import source does
- * not support profiles, this attribute is null.
- */
- readonly attribute jsval sourceProfiles;
-
- /**
- * The import source homepage. Returns null if not present/available
- */
- readonly attribute AUTF8String sourceHomePageURL;
-
-
- /**
- * Whether the source browser data is locked/in-use meaning migration likely
- * won't succeed and the user should be warned.
- */
- readonly attribute boolean sourceLocked;
-};
diff --git a/browser/components/migration/nsIEHistoryEnumerator.cpp b/browser/components/migration/nsIEHistoryEnumerator.cpp
deleted file mode 100644
index 0b377d27e..000000000
--- a/browser/components/migration/nsIEHistoryEnumerator.cpp
+++ /dev/null
@@ -1,119 +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/. */
-
-#include "nsIEHistoryEnumerator.h"
-
-#include <urlhist.h>
-#include <shlguid.h>
-
-#include "nsArrayEnumerator.h"
-#include "nsCOMArray.h"
-#include "nsIVariant.h"
-#include "nsNetUtil.h"
-#include "nsStringAPI.h"
-#include "nsWindowsMigrationUtils.h"
-#include "prtime.h"
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIEHistoryEnumerator
-
-NS_IMPL_ISUPPORTS(nsIEHistoryEnumerator, nsISimpleEnumerator)
-
-nsIEHistoryEnumerator::nsIEHistoryEnumerator()
-{
- ::CoInitialize(nullptr);
-}
-
-nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
-{
- ::CoUninitialize();
-}
-
-void
-nsIEHistoryEnumerator::EnsureInitialized()
-{
- if (mURLEnumerator)
- return;
-
- HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_IUrlHistoryStg2,
- getter_AddRefs(mIEHistory));
- if (FAILED(hr))
- return;
-
- hr = mIEHistory->EnumUrls(getter_AddRefs(mURLEnumerator));
- if (FAILED(hr))
- return;
-}
-
-NS_IMETHODIMP
-nsIEHistoryEnumerator::HasMoreElements(bool* _retval)
-{
- *_retval = false;
-
- EnsureInitialized();
- MOZ_ASSERT(mURLEnumerator, "Should have instanced an IE History URLEnumerator");
- if (!mURLEnumerator)
- return NS_OK;
-
- STATURL statURL;
- ULONG fetched;
-
- // First argument is not implemented, so doesn't matter what we pass.
- HRESULT hr = mURLEnumerator->Next(1, &statURL, &fetched);
- if (FAILED(hr) || fetched != 1UL) {
- // Reached the last entry.
- return NS_OK;
- }
-
- nsCOMPtr<nsIURI> uri;
- if (statURL.pwcsUrl) {
- nsDependentString url(statURL.pwcsUrl);
- nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
- ::CoTaskMemFree(statURL.pwcsUrl);
- if (NS_FAILED(rv)) {
- // Got a corrupt or invalid URI, continue to the next entry.
- return HasMoreElements(_retval);
- }
- }
-
- nsDependentString title(statURL.pwcsTitle ? statURL.pwcsTitle : L"");
-
- bool lastVisitTimeIsValid;
- PRTime lastVisited = WinMigrationFileTimeToPRTime(&(statURL.ftLastVisited), &lastVisitTimeIsValid);
-
- mCachedNextEntry = do_CreateInstance("@mozilla.org/hash-property-bag;1");
- MOZ_ASSERT(mCachedNextEntry, "Should have instanced a new property bag");
- if (mCachedNextEntry) {
- mCachedNextEntry->SetPropertyAsInterface(NS_LITERAL_STRING("uri"), uri);
- mCachedNextEntry->SetPropertyAsAString(NS_LITERAL_STRING("title"), title);
- if (lastVisitTimeIsValid) {
- mCachedNextEntry->SetPropertyAsInt64(NS_LITERAL_STRING("time"), lastVisited);
- }
-
- *_retval = true;
- }
-
- if (statURL.pwcsTitle)
- ::CoTaskMemFree(statURL.pwcsTitle);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsIEHistoryEnumerator::GetNext(nsISupports** _retval)
-{
- *_retval = nullptr;
-
- if (!mCachedNextEntry)
- return NS_ERROR_FAILURE;
-
- NS_ADDREF(*_retval = mCachedNextEntry);
- // Release the cached entry, so it can't be returned twice.
- mCachedNextEntry = nullptr;
-
- return NS_OK;
-}
diff --git a/browser/components/migration/nsIEHistoryEnumerator.h b/browser/components/migration/nsIEHistoryEnumerator.h
deleted file mode 100644
index 1572a8dd5..000000000
--- a/browser/components/migration/nsIEHistoryEnumerator.h
+++ /dev/null
@@ -1,37 +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/. */
-
-#ifndef iehistoryenumerator___h___
-#define iehistoryenumerator___h___
-
-#include <urlhist.h>
-
-#include "mozilla/Attributes.h"
-#include "nsCOMPtr.h"
-#include "nsISimpleEnumerator.h"
-#include "nsIWritablePropertyBag2.h"
-
-class nsIEHistoryEnumerator final : public nsISimpleEnumerator
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSISIMPLEENUMERATOR
-
- nsIEHistoryEnumerator();
-
-private:
- ~nsIEHistoryEnumerator();
-
- /**
- * Initializes the history reader, if needed.
- */
- void EnsureInitialized();
-
- RefPtr<IUrlHistoryStg2> mIEHistory;
- RefPtr<IEnumSTATURL> mURLEnumerator;
-
- nsCOMPtr<nsIWritablePropertyBag2> mCachedNextEntry;
-};
-
-#endif
diff --git a/browser/components/migration/nsWindowsMigrationUtils.h b/browser/components/migration/nsWindowsMigrationUtils.h
deleted file mode 100644
index 0288d93d3..000000000
--- a/browser/components/migration/nsWindowsMigrationUtils.h
+++ /dev/null
@@ -1,36 +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/. */
-
-#ifndef windowsmigrationutils__h__
-#define windowsmigrationutils__h__
-
-#include "prtime.h"
-
-static
-PRTime WinMigrationFileTimeToPRTime(FILETIME* filetime, bool* isValid)
-{
- SYSTEMTIME st;
- *isValid = ::FileTimeToSystemTime(filetime, &st);
- if (!*isValid) {
- return 0;
- }
- PRExplodedTime prt;
- prt.tm_year = st.wYear;
- // SYSTEMTIME's day-of-month parameter is 1-based,
- // PRExplodedTime's is 0-based.
- prt.tm_month = st.wMonth - 1;
- prt.tm_mday = st.wDay;
- prt.tm_hour = st.wHour;
- prt.tm_min = st.wMinute;
- prt.tm_sec = st.wSecond;
- prt.tm_usec = st.wMilliseconds * 1000;
- prt.tm_wday = 0;
- prt.tm_yday = 0;
- prt.tm_params.tp_gmt_offset = 0;
- prt.tm_params.tp_dst_offset = 0;
- return PR_ImplodeTime(&prt);
-}
-
-#endif
-