diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 13:17:38 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-06-04 13:17:38 +0200 |
commit | a1be17c1cea81ebb1e8b131a662c698d78f3f7f2 (patch) | |
tree | a92f7de513be600cc07bac458183e9af40e00c06 /browser/components/migration | |
parent | bf11fdd304898ac675e39b01b280d39550e419d0 (diff) | |
download | UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.gz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.lz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.tar.xz UXP-a1be17c1cea81ebb1e8b131a662c698d78f3f7f2.zip |
Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk
Diffstat (limited to 'browser/components/migration')
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 - |