Bug 1802961 - Convert MigratorPrototype into an ES6 class and move into its own ESM as MigratorBase to be subclassed. r=NeilDeakin
Differential Revision: https://phabricator.services.mozilla.com/D163257
This commit is contained in:
@@ -11,13 +11,9 @@ const AUTH_TYPE = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||||
|
|
||||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||||
|
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
|
||||||
import {
|
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||||
MigratorPrototype,
|
|
||||||
MigrationUtils,
|
|
||||||
} from "resource:///modules/MigrationUtils.sys.mjs";
|
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
@@ -72,16 +68,32 @@ function convertBookmarks(items, errorAccumulator) {
|
|||||||
return itemsToInsert;
|
return itemsToInsert;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChromeProfileMigrator() {
|
/**
|
||||||
this._chromeUserDataPathSuffix = "Chrome";
|
* Chrome profile migrator. This can also be used as a parent class for
|
||||||
}
|
* migrators for browsers that are variants of Chrome.
|
||||||
|
*/
|
||||||
|
export class ChromeProfileMigrator extends MigratorBase {
|
||||||
|
get classDescription() {
|
||||||
|
return "Chrome Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype = Object.create(MigratorPrototype);
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
|
||||||
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype._keychainServiceName = "Chrome Safe Storage";
|
get classID() {
|
||||||
ChromeProfileMigrator.prototype._keychainAccountName = "Chrome";
|
return Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
|
||||||
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype._getChromeUserDataPathIfExists = async function() {
|
get _chromeUserDataPathSuffix() {
|
||||||
|
return "Chrome";
|
||||||
|
}
|
||||||
|
|
||||||
|
_keychainServiceName = "Chrome Safe Storage";
|
||||||
|
|
||||||
|
_keychainAccountName = "Chrome";
|
||||||
|
|
||||||
|
async _getChromeUserDataPathIfExists() {
|
||||||
if (this._chromeUserDataPath) {
|
if (this._chromeUserDataPath) {
|
||||||
return this._chromeUserDataPath;
|
return this._chromeUserDataPath;
|
||||||
}
|
}
|
||||||
@@ -95,11 +107,9 @@ ChromeProfileMigrator.prototype._getChromeUserDataPathIfExists = async function(
|
|||||||
this._chromeUserDataPath = null;
|
this._chromeUserDataPath = null;
|
||||||
}
|
}
|
||||||
return this._chromeUserDataPath;
|
return this._chromeUserDataPath;
|
||||||
};
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype.getResources = async function Chrome_getResources(
|
async getResources(aProfile) {
|
||||||
aProfile
|
|
||||||
) {
|
|
||||||
let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
|
let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
|
||||||
if (chromeUserDataPath) {
|
if (chromeUserDataPath) {
|
||||||
let profileFolder = chromeUserDataPath;
|
let profileFolder = chromeUserDataPath;
|
||||||
@@ -122,9 +132,9 @@ ChromeProfileMigrator.prototype.getResources = async function Chrome_getResource
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
};
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype.getLastUsedDate = async function Chrome_getLastUsedDate() {
|
async getLastUsedDate() {
|
||||||
let sourceProfiles = await this.getSourceProfiles();
|
let sourceProfiles = await this.getSourceProfiles();
|
||||||
let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
|
let chromeUserDataPath = await this._getChromeUserDataPathIfExists();
|
||||||
if (!chromeUserDataPath) {
|
if (!chromeUserDataPath) {
|
||||||
@@ -145,9 +155,9 @@ ChromeProfileMigrator.prototype.getLastUsedDate = async function Chrome_getLastU
|
|||||||
let datesOuter = await Promise.all(datePromises);
|
let datesOuter = await Promise.all(datePromises);
|
||||||
datesOuter.push(0);
|
datesOuter.push(0);
|
||||||
return new Date(Math.max(...datesOuter));
|
return new Date(Math.max(...datesOuter));
|
||||||
};
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype.getSourceProfiles = async function Chrome_getSourceProfiles() {
|
async getSourceProfiles() {
|
||||||
if ("__sourceProfiles" in this) {
|
if ("__sourceProfiles" in this) {
|
||||||
return this.__sourceProfiles;
|
return this.__sourceProfiles;
|
||||||
}
|
}
|
||||||
@@ -201,7 +211,154 @@ ChromeProfileMigrator.prototype.getSourceProfiles = async function Chrome_getSou
|
|||||||
}, this)
|
}, this)
|
||||||
.map(({ profile }) => profile);
|
.map(({ profile }) => profile);
|
||||||
return this.__sourceProfiles;
|
return this.__sourceProfiles;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
async _GetPasswordsResource(aProfileFolder) {
|
||||||
|
let loginPath = PathUtils.join(aProfileFolder, "Login Data");
|
||||||
|
if (!(await IOUtils.exists(loginPath))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
_chromeUserDataPathSuffix,
|
||||||
|
_keychainServiceName,
|
||||||
|
_keychainAccountName,
|
||||||
|
_keychainMockPassphrase = null,
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: MigrationUtils.resourceTypes.PASSWORDS,
|
||||||
|
|
||||||
|
async migrate(aCallback) {
|
||||||
|
let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
|
||||||
|
loginPath,
|
||||||
|
"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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no relevant rows, return before initializing crypto and
|
||||||
|
// thus prompting for Keychain access on macOS.
|
||||||
|
if (!rows.length) {
|
||||||
|
aCallback(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let crypto;
|
||||||
|
try {
|
||||||
|
if (AppConstants.platform == "win") {
|
||||||
|
let { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule(
|
||||||
|
"resource:///modules/ChromeWindowsLoginCrypto.sys.mjs"
|
||||||
|
);
|
||||||
|
crypto = new ChromeWindowsLoginCrypto(_chromeUserDataPathSuffix);
|
||||||
|
} else if (AppConstants.platform == "macosx") {
|
||||||
|
let { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule(
|
||||||
|
"resource:///modules/ChromeMacOSLoginCrypto.sys.mjs"
|
||||||
|
);
|
||||||
|
crypto = new ChromeMacOSLoginCrypto(
|
||||||
|
_keychainServiceName,
|
||||||
|
_keychainAccountName,
|
||||||
|
_keychainMockPassphrase
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
aCallback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
// Handle the user canceling Keychain access or other OSCrypto errors.
|
||||||
|
Cu.reportError(ex);
|
||||||
|
aCallback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let logins = [];
|
||||||
|
let fallbackCreationDate = new Date();
|
||||||
|
for (let row of rows) {
|
||||||
|
try {
|
||||||
|
let origin_url = lazy.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: await crypto.decryptData(
|
||||||
|
row.getResultByName("password_value"),
|
||||||
|
null
|
||||||
|
),
|
||||||
|
origin: origin_url.prePath,
|
||||||
|
formActionOrigin: null,
|
||||||
|
httpRealm: null,
|
||||||
|
usernameElement: row.getResultByName("username_element"),
|
||||||
|
passwordElement: row.getResultByName("password_element"),
|
||||||
|
timeCreated: lazy.ChromeMigrationUtils.chromeTimeToDate(
|
||||||
|
row.getResultByName("date_created") + 0,
|
||||||
|
fallbackCreationDate
|
||||||
|
).getTime(),
|
||||||
|
timesUsed: row.getResultByName("times_used") + 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (row.getResultByName("scheme")) {
|
||||||
|
case AUTH_TYPE.SCHEME_HTML:
|
||||||
|
let action_url = row.getResultByName("action_url");
|
||||||
|
if (!action_url) {
|
||||||
|
// If there is no action_url, store the wildcard "" value.
|
||||||
|
// See the `formActionOrigin` IDL comments.
|
||||||
|
loginInfo.formActionOrigin = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let action_uri = lazy.NetUtil.newURI(action_url);
|
||||||
|
if (!kValidSchemes.has(action_uri.scheme)) {
|
||||||
|
continue; // This continues the outer for loop.
|
||||||
|
}
|
||||||
|
loginInfo.formActionOrigin = action_uri.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.origin.length + 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
"Login data scheme type not supported: " +
|
||||||
|
row.getResultByName("scheme")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
logins.push(loginInfo);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (logins.length) {
|
||||||
|
await MigrationUtils.insertLoginsWrapper(logins);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
if (crypto.finalize) {
|
||||||
|
crypto.finalize();
|
||||||
|
}
|
||||||
|
aCallback(true);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function GetBookmarksResource(aProfileFolder, aBrowserKey) {
|
async function GetBookmarksResource(aProfileFolder, aBrowserKey) {
|
||||||
let bookmarksPath = PathUtils.join(aProfileFolder, "Bookmarks");
|
let bookmarksPath = PathUtils.join(aProfileFolder, "Bookmarks");
|
||||||
@@ -458,328 +615,250 @@ async function GetCookiesResource(aProfileFolder) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ChromeProfileMigrator.prototype._GetPasswordsResource = async function(
|
|
||||||
aProfileFolder
|
|
||||||
) {
|
|
||||||
let loginPath = PathUtils.join(aProfileFolder, "Login Data");
|
|
||||||
if (!(await IOUtils.exists(loginPath))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let {
|
|
||||||
_chromeUserDataPathSuffix,
|
|
||||||
_keychainServiceName,
|
|
||||||
_keychainAccountName,
|
|
||||||
_keychainMockPassphrase = null,
|
|
||||||
} = this;
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: MigrationUtils.resourceTypes.PASSWORDS,
|
|
||||||
|
|
||||||
async migrate(aCallback) {
|
|
||||||
let rows = await MigrationUtils.getRowsFromDBWithoutLocks(
|
|
||||||
loginPath,
|
|
||||||
"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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no relevant rows, return before initializing crypto and
|
|
||||||
// thus prompting for Keychain access on macOS.
|
|
||||||
if (!rows.length) {
|
|
||||||
aCallback(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let crypto;
|
|
||||||
try {
|
|
||||||
if (AppConstants.platform == "win") {
|
|
||||||
let { ChromeWindowsLoginCrypto } = ChromeUtils.importESModule(
|
|
||||||
"resource:///modules/ChromeWindowsLoginCrypto.sys.mjs"
|
|
||||||
);
|
|
||||||
crypto = new ChromeWindowsLoginCrypto(_chromeUserDataPathSuffix);
|
|
||||||
} else if (AppConstants.platform == "macosx") {
|
|
||||||
let { ChromeMacOSLoginCrypto } = ChromeUtils.importESModule(
|
|
||||||
"resource:///modules/ChromeMacOSLoginCrypto.sys.mjs"
|
|
||||||
);
|
|
||||||
crypto = new ChromeMacOSLoginCrypto(
|
|
||||||
_keychainServiceName,
|
|
||||||
_keychainAccountName,
|
|
||||||
_keychainMockPassphrase
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
aCallback(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
// Handle the user canceling Keychain access or other OSCrypto errors.
|
|
||||||
Cu.reportError(ex);
|
|
||||||
aCallback(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let logins = [];
|
|
||||||
let fallbackCreationDate = new Date();
|
|
||||||
for (let row of rows) {
|
|
||||||
try {
|
|
||||||
let origin_url = lazy.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: await crypto.decryptData(
|
|
||||||
row.getResultByName("password_value"),
|
|
||||||
null
|
|
||||||
),
|
|
||||||
origin: origin_url.prePath,
|
|
||||||
formActionOrigin: null,
|
|
||||||
httpRealm: null,
|
|
||||||
usernameElement: row.getResultByName("username_element"),
|
|
||||||
passwordElement: row.getResultByName("password_element"),
|
|
||||||
timeCreated: lazy.ChromeMigrationUtils.chromeTimeToDate(
|
|
||||||
row.getResultByName("date_created") + 0,
|
|
||||||
fallbackCreationDate
|
|
||||||
).getTime(),
|
|
||||||
timesUsed: row.getResultByName("times_used") + 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (row.getResultByName("scheme")) {
|
|
||||||
case AUTH_TYPE.SCHEME_HTML:
|
|
||||||
let action_url = row.getResultByName("action_url");
|
|
||||||
if (!action_url) {
|
|
||||||
// If there is no action_url, store the wildcard "" value.
|
|
||||||
// See the `formActionOrigin` IDL comments.
|
|
||||||
loginInfo.formActionOrigin = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let action_uri = lazy.NetUtil.newURI(action_url);
|
|
||||||
if (!kValidSchemes.has(action_uri.scheme)) {
|
|
||||||
continue; // This continues the outer for loop.
|
|
||||||
}
|
|
||||||
loginInfo.formActionOrigin = action_uri.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.origin.length + 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
"Login data scheme type not supported: " +
|
|
||||||
row.getResultByName("scheme")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
logins.push(loginInfo);
|
|
||||||
} catch (e) {
|
|
||||||
Cu.reportError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (logins.length) {
|
|
||||||
await MigrationUtils.insertLoginsWrapper(logins);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
Cu.reportError(e);
|
|
||||||
}
|
|
||||||
if (crypto.finalize) {
|
|
||||||
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
|
* Chromium migrator
|
||||||
*/
|
*/
|
||||||
export function ChromiumProfileMigrator() {
|
export class ChromiumProfileMigrator extends ChromeProfileMigrator {
|
||||||
this._chromeUserDataPathSuffix = "Chromium";
|
get classDescription() {
|
||||||
this._keychainServiceName = "Chromium Safe Storage";
|
return "Chromium Profile Migrator";
|
||||||
this._keychainAccountName = "Chromium";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ChromiumProfileMigrator.prototype = Object.create(
|
get contractID() {
|
||||||
ChromeProfileMigrator.prototype
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
|
||||||
);
|
}
|
||||||
ChromiumProfileMigrator.prototype.classDescription =
|
|
||||||
"Chromium Profile Migrator";
|
get classID() {
|
||||||
ChromiumProfileMigrator.prototype.contractID =
|
return Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}");
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chromium";
|
}
|
||||||
ChromiumProfileMigrator.prototype.classID = Components.ID(
|
|
||||||
"{8cece922-9720-42de-b7db-7cef88cb07ca}"
|
_chromeUserDataPathSuffix = "Chromium";
|
||||||
);
|
_keychainServiceName = "Chromium Safe Storage";
|
||||||
|
_keychainAccountName = "Chromium";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chrome Canary
|
* Chrome Canary
|
||||||
* Not available on Linux
|
* Not available on Linux
|
||||||
*/
|
*/
|
||||||
export function CanaryProfileMigrator() {
|
export class CanaryProfileMigrator extends ChromeProfileMigrator {
|
||||||
this._chromeUserDataPathSuffix = "Canary";
|
get classDescription() {
|
||||||
|
return "Chrome Canary Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=canary";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}");
|
||||||
|
}
|
||||||
|
|
||||||
|
get _chromeUserDataPathSuffix() {
|
||||||
|
return "Canary";
|
||||||
|
}
|
||||||
|
|
||||||
|
get _keychainServiceName() {
|
||||||
|
return "Chromium Safe Storage";
|
||||||
|
}
|
||||||
|
|
||||||
|
get _keychainAccountName() {
|
||||||
|
return "Chromium";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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}"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chrome Dev - Linux only (not available in Mac and Windows)
|
* Chrome Dev - Linux only (not available in Mac and Windows)
|
||||||
*/
|
*/
|
||||||
export function ChromeDevMigrator() {
|
export class ChromeDevMigrator extends ChromeProfileMigrator {
|
||||||
this._chromeUserDataPathSuffix = "Chrome Dev";
|
get classDescription() {
|
||||||
}
|
return "Chrome Dev Profile Migrator";
|
||||||
ChromeDevMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
|
}
|
||||||
ChromeDevMigrator.prototype.classDescription = "Chrome Dev Profile Migrator";
|
|
||||||
ChromeDevMigrator.prototype.contractID =
|
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chrome-dev";
|
|
||||||
ChromeDevMigrator.prototype.classID = Components.ID(
|
|
||||||
"{7370a02a-4886-42c3-a4ec-d48c726ec30a}"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function ChromeBetaMigrator() {
|
get contractID() {
|
||||||
this._chromeUserDataPathSuffix = "Chrome Beta";
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chrome-dev";
|
||||||
}
|
}
|
||||||
ChromeBetaMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
|
|
||||||
ChromeBetaMigrator.prototype.classDescription = "Chrome Beta Profile Migrator";
|
|
||||||
ChromeBetaMigrator.prototype.contractID =
|
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chrome-beta";
|
|
||||||
ChromeBetaMigrator.prototype.classID = Components.ID(
|
|
||||||
"{47f75963-840b-4950-a1f0-d9c1864f8b8e}"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function BraveProfileMigrator() {
|
get classID() {
|
||||||
this._chromeUserDataPathSuffix = "Brave";
|
return Components.ID("{7370a02a-4886-42c3-a4ec-d48c726ec30a}");
|
||||||
this._keychainServiceName = "Brave Browser Safe Storage";
|
}
|
||||||
this._keychainAccountName = "Brave Browser";
|
|
||||||
|
_chromeUserDataPathSuffix = "Chrome Dev";
|
||||||
|
_keychainServiceName = "Chromium Safe Storage";
|
||||||
|
_keychainAccountName = "Chromium";
|
||||||
}
|
}
|
||||||
|
|
||||||
BraveProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
|
/**
|
||||||
BraveProfileMigrator.prototype.classDescription = "Brave Browser Migrator";
|
* Chrome Beta migrator
|
||||||
BraveProfileMigrator.prototype.contractID =
|
*/
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=brave";
|
export class ChromeBetaMigrator extends ChromeProfileMigrator {
|
||||||
BraveProfileMigrator.prototype.classID = Components.ID(
|
get classDescription() {
|
||||||
"{4071880a-69e4-4c83-88b4-6c589a62801d}"
|
return "Chrome Beta Profile Migrator";
|
||||||
);
|
}
|
||||||
|
|
||||||
export function ChromiumEdgeMigrator() {
|
get contractID() {
|
||||||
this._chromeUserDataPathSuffix = "Edge";
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chrome-beta";
|
||||||
this._keychainServiceName = "Microsoft Edge Safe Storage";
|
}
|
||||||
this._keychainAccountName = "Microsoft Edge";
|
|
||||||
}
|
|
||||||
ChromiumEdgeMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
|
|
||||||
ChromiumEdgeMigrator.prototype.classDescription =
|
|
||||||
"Chromium Edge Profile Migrator";
|
|
||||||
ChromiumEdgeMigrator.prototype.contractID =
|
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge";
|
|
||||||
ChromiumEdgeMigrator.prototype.classID = Components.ID(
|
|
||||||
"{3c7f6b7c-baa9-4338-acfa-04bf79f1dcf1}"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function ChromiumEdgeBetaMigrator() {
|
get classID() {
|
||||||
this._chromeUserDataPathSuffix = "Edge Beta";
|
return Components.ID("{47f75963-840b-4950-a1f0-d9c1864f8b8e}");
|
||||||
this._keychainServiceName = "Microsoft Edge Safe Storage";
|
}
|
||||||
this._keychainAccountName = "Microsoft Edge";
|
|
||||||
}
|
|
||||||
ChromiumEdgeBetaMigrator.prototype = Object.create(
|
|
||||||
ChromiumEdgeMigrator.prototype
|
|
||||||
);
|
|
||||||
ChromiumEdgeBetaMigrator.prototype.classDescription =
|
|
||||||
"Chromium Edge Beta Profile Migrator";
|
|
||||||
ChromiumEdgeBetaMigrator.prototype.contractID =
|
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge-beta";
|
|
||||||
ChromiumEdgeBetaMigrator.prototype.classID = Components.ID(
|
|
||||||
"{0fc3d48a-c1c3-4871-b58f-a8b47d1555fb}"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function Chromium360seMigrator() {
|
_chromeUserDataPathSuffix = "Chrome Beta";
|
||||||
this._chromeUserDataPathSuffix = "360 SE";
|
_keychainServiceName = "Chromium Safe Storage";
|
||||||
|
_keychainAccountName = "Chromium";
|
||||||
}
|
}
|
||||||
Chromium360seMigrator.prototype = Object.create(
|
|
||||||
ChromeProfileMigrator.prototype
|
|
||||||
);
|
|
||||||
Chromium360seMigrator.prototype.classDescription =
|
|
||||||
"Chromium 360 Secure Browser Profile Migrator";
|
|
||||||
Chromium360seMigrator.prototype.contractID =
|
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=chromium-360se";
|
|
||||||
Chromium360seMigrator.prototype.classID = Components.ID(
|
|
||||||
"{2e1a182e-ce4f-4dc9-a22c-d4125b931552}"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function OperaProfileMigrator() {
|
/**
|
||||||
this._chromeUserDataPathSuffix = "Opera";
|
* Brave migrator
|
||||||
this._keychainServiceName = "Opera Browser Safe Storage";
|
*/
|
||||||
this._keychainAccountName = "Opera Browser";
|
export class BraveProfileMigrator extends ChromeProfileMigrator {
|
||||||
|
get classDescription() {
|
||||||
|
return "Brave Browser Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=brave";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{4071880a-69e4-4c83-88b4-6c589a62801d}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Brave";
|
||||||
|
_keychainServiceName = "Brave Browser Safe Storage";
|
||||||
|
_keychainAccountName = "Brave Browser";
|
||||||
}
|
}
|
||||||
OperaProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
|
|
||||||
OperaProfileMigrator.prototype.classDescription = "Opera Browser Migrator";
|
/**
|
||||||
OperaProfileMigrator.prototype.contractID =
|
* Edge (Chromium-based) migrator
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=opera";
|
*/
|
||||||
OperaProfileMigrator.prototype.classID = Components.ID(
|
export class ChromiumEdgeMigrator extends ChromeProfileMigrator {
|
||||||
"{16c5d501-e411-41eb-93f2-af6c9ba64dee}"
|
get classDescription() {
|
||||||
);
|
return "Chromium Edge Profile Migrator";
|
||||||
OperaProfileMigrator.prototype.getSourceProfiles = function() {
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{3c7f6b7c-baa9-4338-acfa-04bf79f1dcf1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Edge";
|
||||||
|
_keychainServiceName = "Microsoft Edge Safe Storage";
|
||||||
|
_keychainAccountName = "Microsoft Edge";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edge Beta (Chromium-based) migrator
|
||||||
|
*/
|
||||||
|
export class ChromiumEdgeBetaMigrator extends ChromeProfileMigrator {
|
||||||
|
get classDescription() {
|
||||||
|
return "Chromium Edge Beta Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chromium-edge-beta";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{0fc3d48a-c1c3-4871-b58f-a8b47d1555fb}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Edge Beta";
|
||||||
|
_keychainServiceName = "Microsoft Edge Safe Storage";
|
||||||
|
_keychainAccountName = "Microsoft Edge";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chromium 360 migrator
|
||||||
|
*/
|
||||||
|
export class Chromium360seMigrator extends ChromeProfileMigrator {
|
||||||
|
get classDescription() {
|
||||||
|
return "Chromium 360 Secure Browser Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=chromium-360se";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{2e1a182e-ce4f-4dc9-a22c-d4125b931552}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "360 SE";
|
||||||
|
_keychainServiceName = "Microsoft Edge Safe Storage";
|
||||||
|
_keychainAccountName = "Microsoft Edge";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opera migrator
|
||||||
|
*/
|
||||||
|
export class OperaProfileMigrator extends ChromeProfileMigrator {
|
||||||
|
get classDescription() {
|
||||||
|
return "Opera Browser Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=opera";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{16c5d501-e411-41eb-93f2-af6c9ba64dee}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Opera";
|
||||||
|
_keychainServiceName = "Opera Browser Safe Storage";
|
||||||
|
_keychainAccountName = "Opera Browser";
|
||||||
|
|
||||||
|
getSourceProfiles() {
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function OperaGXProfileMigrator() {
|
|
||||||
this._chromeUserDataPathSuffix = "Opera GX";
|
|
||||||
this._keychainServiceName = "Opera Browser Safe Storage";
|
|
||||||
this._keychainAccountName = "Opera Browser";
|
|
||||||
}
|
}
|
||||||
OperaGXProfileMigrator.prototype = Object.create(
|
|
||||||
ChromeProfileMigrator.prototype
|
/**
|
||||||
);
|
* Opera GX migrator
|
||||||
OperaGXProfileMigrator.prototype.classDescription = "Opera GX Browser Migrator";
|
*/
|
||||||
OperaGXProfileMigrator.prototype.contractID =
|
export class OperaGXProfileMigrator extends ChromeProfileMigrator {
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=opera-gx";
|
get classDescription() {
|
||||||
OperaGXProfileMigrator.prototype.classID = Components.ID(
|
return "Opera GX Browser Migrator";
|
||||||
"{26F4E0A0-B533-4FDA-B344-6FF5DA45D6DC}"
|
}
|
||||||
);
|
|
||||||
OperaGXProfileMigrator.prototype.getSourceProfiles = function() {
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=opera-gx";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{26F4E0A0-B533-4FDA-B344-6FF5DA45D6DC}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Opera GX";
|
||||||
|
_keychainServiceName = "Opera Browser Safe Storage";
|
||||||
|
_keychainAccountName = "Opera Browser";
|
||||||
|
|
||||||
|
getSourceProfiles() {
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function VivaldiProfileMigrator() {
|
|
||||||
this._chromeUserDataPathSuffix = "Vivaldi";
|
|
||||||
this._keychainServiceName = "Vivaldi Safe Storage";
|
|
||||||
this._keychainAccountName = "Vivaldi";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VivaldiProfileMigrator.prototype = Object.create(
|
/**
|
||||||
ChromeProfileMigrator.prototype
|
* Vivaldi migrator
|
||||||
);
|
*/
|
||||||
VivaldiProfileMigrator.prototype.classDescription = "Vivaldi Migrator";
|
export class VivaldiProfileMigrator extends ChromeProfileMigrator {
|
||||||
VivaldiProfileMigrator.prototype.contractID =
|
get classDescription() {
|
||||||
"@mozilla.org/profile/migrator;1?app=browser&type=vivaldi";
|
return "Vivaldi Migrator";
|
||||||
VivaldiProfileMigrator.prototype.classID = Components.ID(
|
}
|
||||||
"{54a6a025-e70d-49dd-ba95-0f7e45d728d3}"
|
|
||||||
);
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=vivaldi";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{54a6a025-e70d-49dd-ba95-0f7e45d728d3}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_chromeUserDataPathSuffix = "Vivaldi";
|
||||||
|
_keychainServiceName = "Vivaldi Safe Storage";
|
||||||
|
_keychainAccountName = "Vivaldi";
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
|||||||
|
|
||||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||||
|
|
||||||
import {
|
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
|
||||||
MigrationUtils,
|
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||||
MigratorPrototype,
|
|
||||||
} from "resource:///modules/MigrationUtils.sys.mjs";
|
|
||||||
import { MSMigrationUtils } from "resource:///modules/MSMigrationUtils.sys.mjs";
|
import { MSMigrationUtils } from "resource:///modules/MSMigrationUtils.sys.mjs";
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
@@ -466,25 +464,36 @@ EdgeBookmarksMigrator.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EdgeProfileMigrator() {
|
/**
|
||||||
|
* Edge (EdgeHTML) profile migrator
|
||||||
|
*/
|
||||||
|
export class EdgeProfileMigrator extends MigratorBase {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
this.wrappedJSObject = this;
|
this.wrappedJSObject = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
|
get classDescription() {
|
||||||
|
return "Edge Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype.getBookmarksMigratorForTesting = function(
|
get contractID() {
|
||||||
dbOverride
|
return "@mozilla.org/profile/migrator;1?app=browser&type=edge";
|
||||||
) {
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{62e8834b-2d17-49f5-96ff-56344903a2ae}");
|
||||||
|
}
|
||||||
|
|
||||||
|
getBookmarksMigratorForTesting(dbOverride) {
|
||||||
return new EdgeBookmarksMigrator(dbOverride);
|
return new EdgeBookmarksMigrator(dbOverride);
|
||||||
};
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype.getReadingListMigratorForTesting = function(
|
getReadingListMigratorForTesting(dbOverride) {
|
||||||
dbOverride
|
|
||||||
) {
|
|
||||||
return new EdgeReadingListMigrator(dbOverride);
|
return new EdgeReadingListMigrator(dbOverride);
|
||||||
};
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype.getResources = function() {
|
getResources() {
|
||||||
let resources = [
|
let resources = [
|
||||||
new EdgeBookmarksMigrator(),
|
new EdgeBookmarksMigrator(),
|
||||||
MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
|
MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
|
||||||
@@ -496,9 +505,9 @@ EdgeProfileMigrator.prototype.getResources = function() {
|
|||||||
windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
|
windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
|
||||||
resources.push(windowsVaultFormPasswordsMigrator);
|
resources.push(windowsVaultFormPasswordsMigrator);
|
||||||
return resources.filter(r => r.exists);
|
return resources.filter(r => r.exists);
|
||||||
};
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype.getLastUsedDate = async function() {
|
async getLastUsedDate() {
|
||||||
// Don't do this if we don't have a single profile (see the comment for
|
// 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:
|
// sourceProfiles) or if we can't find the database file:
|
||||||
let sourceProfiles = await this.getSourceProfiles();
|
let sourceProfiles = await this.getSourceProfiles();
|
||||||
@@ -534,21 +543,18 @@ EdgeProfileMigrator.prototype.getLastUsedDate = async function() {
|
|||||||
return Promise.all(datePromises).then(dates => {
|
return Promise.all(datePromises).then(dates => {
|
||||||
return new Date(Math.max.apply(Math, dates));
|
return new Date(Math.max.apply(Math, dates));
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
/* Somewhat counterintuitively, this returns:
|
/**
|
||||||
|
* @returns {Array|null}
|
||||||
|
* Somewhat counterintuitively, this returns:
|
||||||
* - |null| to indicate "There is only 1 (default) profile" (on win10+)
|
* - |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.
|
* - |[]| to indicate "There are no profiles" (on <=win8.1) which will avoid
|
||||||
* See MigrationUtils.jsm for slightly more info on how sourceProfiles is used.
|
* using this migrator.
|
||||||
|
* See MigrationUtils.sys.mjs for slightly more info on how sourceProfiles is used.
|
||||||
*/
|
*/
|
||||||
EdgeProfileMigrator.prototype.getSourceProfiles = function() {
|
getSourceProfiles() {
|
||||||
let isWin10OrHigher = AppConstants.isPlatformAndVersionAtLeast("win", "10");
|
let isWin10OrHigher = AppConstants.isPlatformAndVersionAtLeast("win", "10");
|
||||||
return isWin10OrHigher ? null : [];
|
return isWin10OrHigher ? null : [];
|
||||||
};
|
}
|
||||||
|
}
|
||||||
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}"
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -11,10 +11,9 @@
|
|||||||
* from the source profile.
|
* from the source profile.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
|
||||||
MigrationUtils,
|
|
||||||
MigratorPrototype,
|
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||||
} from "resource:///modules/MigrationUtils.sys.mjs";
|
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
@@ -25,17 +24,37 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||||||
SessionMigration: "resource:///modules/sessionstore/SessionMigration.sys.mjs",
|
SessionMigration: "resource:///modules/sessionstore/SessionMigration.sys.mjs",
|
||||||
});
|
});
|
||||||
|
|
||||||
export function FirefoxProfileMigrator() {
|
/**
|
||||||
|
* Firefox profile migrator. Currently, this class only does "pave over"
|
||||||
|
* migrations, where various parts of an old profile overwrite a new
|
||||||
|
* profile. This is distinct from other migrators which attempt to import
|
||||||
|
* old profile data into the existing profile.
|
||||||
|
*
|
||||||
|
* This migrator is what powers the "Profile Refresh" mechanism.
|
||||||
|
*/
|
||||||
|
export class FirefoxProfileMigrator extends MigratorBase {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
this.wrappedJSObject = this; // for testing...
|
this.wrappedJSObject = this; // for testing...
|
||||||
}
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype = Object.create(MigratorPrototype);
|
get classDescription() {
|
||||||
|
return "Firefox Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype._getAllProfiles = function() {
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=firefox";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{91185366-ba97-4438-acba-48deaca63386}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAllProfiles() {
|
||||||
let allProfiles = new Map();
|
let allProfiles = new Map();
|
||||||
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].getService(
|
let profileService = Cc[
|
||||||
Ci.nsIToolkitProfileService
|
"@mozilla.org/toolkit/profile-service;1"
|
||||||
);
|
].getService(Ci.nsIToolkitProfileService);
|
||||||
for (let profile of profileService.profiles) {
|
for (let profile of profileService.profiles) {
|
||||||
let rootDir = profile.rootDir;
|
let rootDir = profile.rootDir;
|
||||||
|
|
||||||
@@ -48,19 +67,19 @@ FirefoxProfileMigrator.prototype._getAllProfiles = function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return allProfiles;
|
return allProfiles;
|
||||||
};
|
}
|
||||||
|
|
||||||
function sorter(a, b) {
|
getSourceProfiles() {
|
||||||
|
let sorter = (a, b) => {
|
||||||
return a.id.toLocaleLowerCase().localeCompare(b.id.toLocaleLowerCase());
|
return a.id.toLocaleLowerCase().localeCompare(b.id.toLocaleLowerCase());
|
||||||
}
|
};
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype.getSourceProfiles = function() {
|
|
||||||
return [...this._getAllProfiles().keys()]
|
return [...this._getAllProfiles().keys()]
|
||||||
.map(x => ({ id: x, name: x }))
|
.map(x => ({ id: x, name: x }))
|
||||||
.sort(sorter);
|
.sort(sorter);
|
||||||
};
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype._getFileObject = function(dir, fileName) {
|
_getFileObject(dir, fileName) {
|
||||||
let file = dir.clone();
|
let file = dir.clone();
|
||||||
file.append(fileName);
|
file.append(fileName);
|
||||||
|
|
||||||
@@ -68,9 +87,9 @@ FirefoxProfileMigrator.prototype._getFileObject = function(dir, fileName) {
|
|||||||
// they are not expected to work alone. Return null to avoid trying to
|
// they are not expected to work alone. Return null to avoid trying to
|
||||||
// copy non-existing files.
|
// copy non-existing files.
|
||||||
return file.exists() ? file : null;
|
return file.exists() ? file : null;
|
||||||
};
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
|
getResources(aProfile) {
|
||||||
let sourceProfileDir = aProfile
|
let sourceProfileDir = aProfile
|
||||||
? this._getAllProfiles().get(aProfile.id)
|
? this._getAllProfiles().get(aProfile.id)
|
||||||
: Cc["@mozilla.org/toolkit/profile-service;1"].getService(
|
: Cc["@mozilla.org/toolkit/profile-service;1"].getService(
|
||||||
@@ -94,19 +113,16 @@ FirefoxProfileMigrator.prototype.getResources = function(aProfile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this._getResourcesInternal(sourceProfileDir, currentProfileDir);
|
return this._getResourcesInternal(sourceProfileDir, currentProfileDir);
|
||||||
};
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype.getLastUsedDate = function() {
|
getLastUsedDate() {
|
||||||
// We always pretend we're really old, so that we don't mess
|
// We always pretend we're really old, so that we don't mess
|
||||||
// up the determination of which browser is the most 'recent'
|
// up the determination of which browser is the most 'recent'
|
||||||
// to import from.
|
// to import from.
|
||||||
return Promise.resolve(new Date(0));
|
return Promise.resolve(new Date(0));
|
||||||
};
|
}
|
||||||
|
|
||||||
FirefoxProfileMigrator.prototype._getResourcesInternal = function(
|
_getResourcesInternal(sourceProfileDir, currentProfileDir) {
|
||||||
sourceProfileDir,
|
|
||||||
currentProfileDir
|
|
||||||
) {
|
|
||||||
let getFileResource = (aMigrationType, aFileNames) => {
|
let getFileResource = (aMigrationType, aFileNames) => {
|
||||||
let files = [];
|
let files = [];
|
||||||
for (let fileName of aFileNames) {
|
for (let fileName of aFileNames) {
|
||||||
@@ -351,15 +367,9 @@ FirefoxProfileMigrator.prototype._getResourcesInternal = function(
|
|||||||
telemetry,
|
telemetry,
|
||||||
favicons,
|
favicons,
|
||||||
].filter(r => r);
|
].filter(r => r);
|
||||||
};
|
}
|
||||||
|
|
||||||
Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", {
|
get startupOnlyMigrator() {
|
||||||
get: () => true,
|
return 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}"
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -8,10 +8,8 @@ const kLoginsKey =
|
|||||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||||
|
|
||||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||||
import {
|
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
|
||||||
MigrationUtils,
|
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||||
MigratorPrototype,
|
|
||||||
} from "resource:///modules/MigrationUtils.sys.mjs";
|
|
||||||
import { MSMigrationUtils } from "resource:///modules/MSMigrationUtils.sys.mjs";
|
import { MSMigrationUtils } from "resource:///modules/MSMigrationUtils.sys.mjs";
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
@@ -351,13 +349,28 @@ IE7FormPasswords.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function IEProfileMigrator() {
|
/**
|
||||||
|
* Internet Explorer profile migrator
|
||||||
|
*/
|
||||||
|
export class IEProfileMigrator extends MigratorBase {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
this.wrappedJSObject = this; // export this to be able to use it in the unittest.
|
this.wrappedJSObject = this; // export this to be able to use it in the unittest.
|
||||||
}
|
}
|
||||||
|
|
||||||
IEProfileMigrator.prototype = Object.create(MigratorPrototype);
|
get classDescription() {
|
||||||
|
return "IE Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
IEProfileMigrator.prototype.getResources = function IE_getResources() {
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=ie";
|
||||||
|
}
|
||||||
|
|
||||||
|
get classID() {
|
||||||
|
return Components.ID("{3d2532e3-4932-4774-b7ba-968f5899d3a4}");
|
||||||
|
}
|
||||||
|
|
||||||
|
getResources() {
|
||||||
let resources = [
|
let resources = [
|
||||||
MSMigrationUtils.getBookmarksMigrator(),
|
MSMigrationUtils.getBookmarksMigrator(),
|
||||||
new History(),
|
new History(),
|
||||||
@@ -371,9 +384,9 @@ IEProfileMigrator.prototype.getResources = function IE_getResources() {
|
|||||||
windowsVaultFormPasswordsMigrator.name = "IEVaultFormPasswords";
|
windowsVaultFormPasswordsMigrator.name = "IEVaultFormPasswords";
|
||||||
resources.push(windowsVaultFormPasswordsMigrator);
|
resources.push(windowsVaultFormPasswordsMigrator);
|
||||||
return resources.filter(r => r.exists);
|
return resources.filter(r => r.exists);
|
||||||
};
|
}
|
||||||
|
|
||||||
IEProfileMigrator.prototype.getLastUsedDate = function IE_getLastUsedDate() {
|
getLastUsedDate() {
|
||||||
let datePromises = ["Favs", "CookD"].map(dirId => {
|
let datePromises = ["Favs", "CookD"].map(dirId => {
|
||||||
let { path } = Services.dirsvc.get(dirId, Ci.nsIFile);
|
let { path } = Services.dirsvc.get(dirId, Ci.nsIFile);
|
||||||
return OS.File.stat(path)
|
return OS.File.stat(path)
|
||||||
@@ -398,11 +411,5 @@ IEProfileMigrator.prototype.getLastUsedDate = function IE_getLastUsedDate() {
|
|||||||
return Promise.all(datePromises).then(dates => {
|
return Promise.all(datePromises).then(dates => {
|
||||||
return new Date(Math.max.apply(Math, dates));
|
return new Date(Math.max.apply(Math, dates));
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}
|
||||||
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}"
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -2,22 +2,13 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
ChromeUtils.defineESModuleGetters(lazy, {
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
|
|
||||||
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
|
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
|
||||||
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
|
PlacesUIUtils: "resource:///modules/PlacesUIUtils.sys.mjs",
|
||||||
PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs",
|
|
||||||
ResponsivenessMonitor: "resource://gre/modules/ResponsivenessMonitor.sys.mjs",
|
|
||||||
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
|
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
|
||||||
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||||
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
|
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
|
||||||
@@ -44,475 +35,12 @@ function getL10n() {
|
|||||||
return gL10n;
|
return gL10n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} MigratorResource
|
|
||||||
* A resource returned by a subclass of MigratorPrototype that can migrate
|
|
||||||
* data to this browser.
|
|
||||||
* @property {number} type
|
|
||||||
* A bitfield with bits from nsIBrowserProfileMigrator flipped to indicate
|
|
||||||
* what this resource represents. A resource can represent one or more types
|
|
||||||
* of data, for example HISTORY and FORMDATA.
|
|
||||||
* @property {Function} migrate
|
|
||||||
* A function that will actually perform the migration of this resource's
|
|
||||||
* data into this browser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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. For startup-only migrators, override |startupOnlyMigrator|.
|
|
||||||
*/
|
|
||||||
export var MigratorPrototype = {
|
|
||||||
QueryInterface: ChromeUtils.generateQI(["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.
|
|
||||||
*
|
|
||||||
* @abstract
|
|
||||||
* @returns {object[]|null}
|
|
||||||
*/
|
|
||||||
getSourceProfiles() {
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @abstract
|
|
||||||
* @param {object|string} 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).
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
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).
|
|
||||||
*
|
|
||||||
* If not overridden, the promise will resolve to the Unix epoch.
|
|
||||||
*
|
|
||||||
* @returns {Promise<Date>}
|
|
||||||
* A Promise that resolves to the last used date.
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @returns {boolean}
|
|
||||||
* true if the migrator is start-up only.
|
|
||||||
*/
|
|
||||||
get startupOnlyMigrator() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the migrator is configured to be enabled. This is
|
|
||||||
* controlled by the `browser.migrate.<BROWSER_KEY>.enabled` boolean
|
|
||||||
* preference.
|
|
||||||
*
|
|
||||||
* @returns {boolean}
|
|
||||||
* true if the migrator should be shown in the migration wizard.
|
|
||||||
*/
|
|
||||||
get enabled() {
|
|
||||||
let key = this.getBrowserKey();
|
|
||||||
return Services.prefs.getBoolPref(`browser.migrate.${key}.enabled`, false);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
|
|
||||||
* getResources.
|
|
||||||
*
|
|
||||||
* @see nsIBrowserProfileMigrator
|
|
||||||
* @param {object|string} aProfile
|
|
||||||
* The profile from which data may be imported, or an empty string
|
|
||||||
* in the case of a single-profile migrator.
|
|
||||||
* @returns {MigratorResource[]}
|
|
||||||
*/
|
|
||||||
getMigrateData: async function MP_getMigrateData(aProfile) {
|
|
||||||
let resources = await this._getMaybeCachedResources(aProfile);
|
|
||||||
if (!resources) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
* @param {number} aItems
|
|
||||||
* A bitfield with bits from nsIBrowserProfileMigrator flipped to indicate
|
|
||||||
* what types of resources should be migrated.
|
|
||||||
* @param {boolean} aStartup
|
|
||||||
* True if this migration is occurring during startup.
|
|
||||||
* @param {object|string} aProfile
|
|
||||||
* The other browser profile that is being migrated from.
|
|
||||||
*/
|
|
||||||
migrate: async function MP_migrate(aItems, aStartup, aProfile) {
|
|
||||||
let resources = await this._getMaybeCachedResources(aProfile);
|
|
||||||
if (!resources.length) {
|
|
||||||
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.dispatchToMainThread(resolve);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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 lazy.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 = 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 = lazy.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
await completeDeferred.promise;
|
|
||||||
await unblockMainThread();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
MigrationUtils.isStartupMigration &&
|
|
||||||
!this.startupOnlyMigrator &&
|
|
||||||
Services.policies.isAllowed("defaultBookmarks")
|
|
||||||
) {
|
|
||||||
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.
|
|
||||||
(async 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.
|
|
||||||
await lazy.BookmarkHTMLUtils.importFromURL(
|
|
||||||
"chrome://browser/content/default-bookmarks.html",
|
|
||||||
{
|
|
||||||
replace: true,
|
|
||||||
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
|
|
||||||
}
|
|
||||||
).catch(Cu.reportError);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
);
|
|
||||||
});
|
|
||||||
browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
|
|
||||||
await placesInitedPromise;
|
|
||||||
doMigrate();
|
|
||||||
})();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doMigrate();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DO NOT OVERRIDE - After deCOMing migration, this code
|
|
||||||
* won't be part of the migrator itself.
|
|
||||||
*
|
|
||||||
* @see nsIBrowserProfileMigrator
|
|
||||||
*/
|
|
||||||
async isSourceAvailable() {
|
|
||||||
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 = await this.getSourceProfiles();
|
|
||||||
if (!profiles) {
|
|
||||||
let resources = await this._getMaybeCachedResources("");
|
|
||||||
if (resources && resources.length) {
|
|
||||||
exists = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
exists = !!profiles.length;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
Cu.reportError(ex);
|
|
||||||
}
|
|
||||||
return exists;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*** PRIVATE STUFF - DO NOT OVERRIDE ***/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns resources for a particular profile and then caches them for later
|
|
||||||
* lookups.
|
|
||||||
*
|
|
||||||
* @param {object|string} aProfile
|
|
||||||
* The profile that resources are being imported from.
|
|
||||||
* @returns {Promise<MigrationResource[]>}
|
|
||||||
*/
|
|
||||||
_getMaybeCachedResources: async 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] = await this.getResources(aProfile);
|
|
||||||
return this._resourcesByProfile[profileKey];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The singleton MigrationUtils service. This service is the primary mechanism
|
* The singleton MigrationUtils service. This service is the primary mechanism
|
||||||
* by which migrations from other browsers to this browser occur. The singleton
|
* by which migrations from other browsers to this browser occur. The singleton
|
||||||
* instance of this class is exported from this module as `MigrationUtils`.
|
* instance of this class is exported from this module as `MigrationUtils`.
|
||||||
*/
|
*/
|
||||||
class MigrationUtilsSingleton {
|
class MigrationUtils {
|
||||||
resourceTypes = Object.freeze({
|
resourceTypes = Object.freeze({
|
||||||
COOKIES: Ci.nsIBrowserProfileMigrator.COOKIES,
|
COOKIES: Ci.nsIBrowserProfileMigrator.COOKIES,
|
||||||
HISTORY: Ci.nsIBrowserProfileMigrator.HISTORY,
|
HISTORY: Ci.nsIBrowserProfileMigrator.HISTORY,
|
||||||
@@ -525,7 +53,7 @@ class MigrationUtilsSingleton {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for implementing simple asynchronous cases of migration resources'
|
* Helper for implementing simple asynchronous cases of migration resources'
|
||||||
* |migrate(aCallback)| (see MigratorPrototype). If your |migrate| method
|
* |migrate(aCallback)| (see MigratorBase). If your |migrate| method
|
||||||
* just waits for some file to be read, for example, and then migrates
|
* 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
|
* everything right away, you can wrap the async-function with this helper
|
||||||
* and not worry about notifying the callback.
|
* and not worry about notifying the callback.
|
||||||
@@ -711,7 +239,7 @@ class MigrationUtilsSingleton {
|
|||||||
* Internal name of the migration source. See `availableMigratorKeys`
|
* Internal name of the migration source. See `availableMigratorKeys`
|
||||||
* for supported values by OS.
|
* for supported values by OS.
|
||||||
*
|
*
|
||||||
* @returns {MigratorPrototype}
|
* @returns {MigratorBase}
|
||||||
* A profile migrator implementing nsIBrowserProfileMigrator, if it can
|
* A profile migrator implementing nsIBrowserProfileMigrator, if it can
|
||||||
* import any data, null otherwise.
|
* import any data, null otherwise.
|
||||||
*/
|
*/
|
||||||
@@ -1420,4 +948,6 @@ class MigrationUtilsSingleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MigrationUtils = new MigrationUtilsSingleton();
|
const MigrationUtilsSingleton = new MigrationUtils();
|
||||||
|
|
||||||
|
export { MigrationUtilsSingleton as MigrationUtils };
|
||||||
|
|||||||
489
browser/components/migration/MigratorBase.sys.mjs
Normal file
489
browser/components/migration/MigratorBase.sys.mjs
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
const lazy = {};
|
||||||
|
|
||||||
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.sys.mjs",
|
||||||
|
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
|
||||||
|
PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
|
||||||
|
PromiseUtils: "resource://gre/modules/PromiseUtils.sys.mjs",
|
||||||
|
ResponsivenessMonitor: "resource://gre/modules/ResponsivenessMonitor.sys.mjs",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} MigratorResource
|
||||||
|
* A resource returned by a subclass of MigratorBase that can migrate
|
||||||
|
* data to this browser.
|
||||||
|
* @property {number} type
|
||||||
|
* A bitfield with bits from nsIBrowserProfileMigrator flipped to indicate
|
||||||
|
* what this resource represents. A resource can represent one or more types
|
||||||
|
* of data, for example HISTORY and FORMDATA.
|
||||||
|
* @property {Function} migrate
|
||||||
|
* A function that will actually perform the migration of this resource's
|
||||||
|
* data into this browser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
|
||||||
|
*
|
||||||
|
* To implement a migrator:
|
||||||
|
* 1. Import this module.
|
||||||
|
* 2. Create the prototype for the migrator, extending MigratorBase.
|
||||||
|
* 3. Set classDescription, contractID and classID for your migrator, and update
|
||||||
|
* components.conf to register the migrator as an XPCOM component.
|
||||||
|
* 4. If the migrator supports multiple profiles, override the sourceProfiles
|
||||||
|
* Here we default for single-profile migrator.
|
||||||
|
* 5. Implement getResources(aProfile) (see below).
|
||||||
|
* 6. For startup-only migrators, override |startupOnlyMigrator|.
|
||||||
|
*/
|
||||||
|
export class MigratorBase {
|
||||||
|
QueryInterface = ChromeUtils.generateQI(["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.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @returns {object[]|null}
|
||||||
|
*/
|
||||||
|
getSourceProfiles() {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {object|string} 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).
|
||||||
|
* @returns {Promise<MigratorResource[]>|MigratorResource[]}
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
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).
|
||||||
|
*
|
||||||
|
* If not overridden, the promise will resolve to the Unix epoch.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Date>}
|
||||||
|
* A Promise that resolves to the last used date.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* true if the migrator is start-up only.
|
||||||
|
*/
|
||||||
|
get startupOnlyMigrator() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the migrator is configured to be enabled. This is
|
||||||
|
* controlled by the `browser.migrate.<BROWSER_KEY>.enabled` boolean
|
||||||
|
* preference.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* true if the migrator should be shown in the migration wizard.
|
||||||
|
*/
|
||||||
|
get enabled() {
|
||||||
|
let key = this.getBrowserKey();
|
||||||
|
return Services.prefs.getBoolPref(`browser.migrate.${key}.enabled`, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
|
||||||
|
* getResources.
|
||||||
|
*
|
||||||
|
* See nsIBrowserProfileMigrator.
|
||||||
|
*
|
||||||
|
* @param {object|string} aProfile
|
||||||
|
* The profile from which data may be imported, or an empty string
|
||||||
|
* in the case of a single-profile migrator.
|
||||||
|
* @returns {MigratorResource[]}
|
||||||
|
*/
|
||||||
|
async getMigrateData(aProfile) {
|
||||||
|
let resources = await this.#getMaybeCachedResources(aProfile);
|
||||||
|
if (!resources) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let types = resources.map(r => r.type);
|
||||||
|
return types.reduce((a, b) => {
|
||||||
|
a |= b;
|
||||||
|
return a;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBrowserKey() {
|
||||||
|
return this.contractID.match(/\=([^\=]+)$/)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT OVERRIDE - After deCOMing migration, the UI will just call
|
||||||
|
* migrate for each resource.
|
||||||
|
*
|
||||||
|
* See nsIBrowserProfileMigrator.
|
||||||
|
*
|
||||||
|
* @param {number} aItems
|
||||||
|
* A bitfield with bits from nsIBrowserProfileMigrator flipped to indicate
|
||||||
|
* what types of resources should be migrated.
|
||||||
|
* @param {boolean} aStartup
|
||||||
|
* True if this migration is occurring during startup.
|
||||||
|
* @param {object|string} aProfile
|
||||||
|
* The other browser profile that is being migrated from.
|
||||||
|
*/
|
||||||
|
async migrate(aItems, aStartup, aProfile) {
|
||||||
|
let resources = await this.#getMaybeCachedResources(aProfile);
|
||||||
|
if (!resources.length) {
|
||||||
|
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.dispatchToMainThread(resolve);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let getHistogramIdForResourceType = (resourceType, template) => {
|
||||||
|
if (resourceType == lazy.MigrationUtils.resourceTypes.HISTORY) {
|
||||||
|
return template.replace("*", "HISTORY");
|
||||||
|
}
|
||||||
|
if (resourceType == lazy.MigrationUtils.resourceTypes.BOOKMARKS) {
|
||||||
|
return template.replace("*", "BOOKMARKS");
|
||||||
|
}
|
||||||
|
if (resourceType == lazy.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 lazy.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(
|
||||||
|
lazy.MigrationUtils._importQuantities
|
||||||
|
)) {
|
||||||
|
let histogramId =
|
||||||
|
"FX_MIGRATION_" + resourceType.toUpperCase() + "_QUANTITY";
|
||||||
|
try {
|
||||||
|
Services.telemetry
|
||||||
|
.getKeyedHistogramById(histogramId)
|
||||||
|
.add(
|
||||||
|
browserKey,
|
||||||
|
lazy.MigrationUtils._importQuantities[resourceType]
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(histogramId + ": " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called either directly or through the bookmarks import callback.
|
||||||
|
let doMigrate = 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(
|
||||||
|
lazy.MigrationUtils._importQuantities
|
||||||
|
)) {
|
||||||
|
lazy.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 = lazy.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
await completeDeferred.promise;
|
||||||
|
await unblockMainThread();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
lazy.MigrationUtils.isStartupMigration &&
|
||||||
|
!this.startupOnlyMigrator &&
|
||||||
|
Services.policies.isAllowed("defaultBookmarks")
|
||||||
|
) {
|
||||||
|
lazy.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.
|
||||||
|
(async 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.
|
||||||
|
await lazy.BookmarkHTMLUtils.importFromURL(
|
||||||
|
"chrome://browser/content/default-bookmarks.html",
|
||||||
|
{
|
||||||
|
replace: true,
|
||||||
|
source: lazy.PlacesUtils.bookmarks.SOURCES.RESTORE_ON_STARTUP,
|
||||||
|
}
|
||||||
|
).catch(Cu.reportError);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
});
|
||||||
|
browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
|
||||||
|
await placesInitedPromise;
|
||||||
|
doMigrate();
|
||||||
|
})();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doMigrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT OVERRIDE - After deCOMing migration, this code
|
||||||
|
* won't be part of the migrator itself.
|
||||||
|
*
|
||||||
|
* See nsIBrowserProfileMigrator.
|
||||||
|
*/
|
||||||
|
async isSourceAvailable() {
|
||||||
|
if (this.startupOnlyMigrator && !lazy.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 = await this.getSourceProfiles();
|
||||||
|
if (!profiles) {
|
||||||
|
let resources = await this.#getMaybeCachedResources("");
|
||||||
|
if (resources && resources.length) {
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exists = !!profiles.length;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** PRIVATE STUFF - DO NOT OVERRIDE ***/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns resources for a particular profile and then caches them for later
|
||||||
|
* lookups.
|
||||||
|
*
|
||||||
|
* @param {object|string} aProfile
|
||||||
|
* The profile that resources are being imported from.
|
||||||
|
* @returns {Promise<MigrationResource[]>}
|
||||||
|
*/
|
||||||
|
async #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] = await this.getResources(aProfile);
|
||||||
|
return this._resourcesByProfile[profileKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,8 @@
|
|||||||
import { FileUtils } from "resource://gre/modules/FileUtils.sys.mjs";
|
import { FileUtils } from "resource://gre/modules/FileUtils.sys.mjs";
|
||||||
|
|
||||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||||
import {
|
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
|
||||||
MigrationUtils,
|
import { MigratorBase } from "resource:///modules/MigratorBase.sys.mjs";
|
||||||
MigratorPrototype,
|
|
||||||
} from "resource:///modules/MigrationUtils.sys.mjs";
|
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
@@ -344,11 +342,23 @@ SearchStrings.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SafariProfileMigrator() {}
|
/**
|
||||||
|
* Safari migrator
|
||||||
|
*/
|
||||||
|
export class SafariProfileMigrator extends MigratorBase {
|
||||||
|
get classDescription() {
|
||||||
|
return "Safari Profile Migrator";
|
||||||
|
}
|
||||||
|
|
||||||
SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
|
get contractID() {
|
||||||
|
return "@mozilla.org/profile/migrator;1?app=browser&type=safari";
|
||||||
|
}
|
||||||
|
|
||||||
SafariProfileMigrator.prototype.getResources = function SM_getResources() {
|
get classID() {
|
||||||
|
return Components.ID("{4b609ecf-60b2-4655-9df4-dc149e474da1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
getResources() {
|
||||||
let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
|
let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
|
||||||
if (!profileDir.exists()) {
|
if (!profileDir.exists()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -379,9 +389,9 @@ SafariProfileMigrator.prototype.getResources = function SM_getResources() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return resources;
|
return resources;
|
||||||
};
|
}
|
||||||
|
|
||||||
SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate() {
|
getLastUsedDate() {
|
||||||
let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
|
let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
|
||||||
let datePromises = ["Bookmarks.plist", "History.plist"].map(file => {
|
let datePromises = ["Bookmarks.plist", "History.plist"].map(file => {
|
||||||
let path = OS.Path.join(profileDir.path, file);
|
let path = OS.Path.join(profileDir.path, file);
|
||||||
@@ -394,9 +404,9 @@ SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate()
|
|||||||
return Promise.all(datePromises).then(dates => {
|
return Promise.all(datePromises).then(dates => {
|
||||||
return new Date(Math.max.apply(Math, dates));
|
return new Date(Math.max.apply(Math, dates));
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
SafariProfileMigrator.prototype.hasPermissions = async function SM_hasPermissions() {
|
async hasPermissions() {
|
||||||
if (this._hasPermissions) {
|
if (this._hasPermissions) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -415,11 +425,9 @@ SafariProfileMigrator.prototype.hasPermissions = async function SM_hasPermission
|
|||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
SafariProfileMigrator.prototype.getPermissions = async function SM_getPermissions(
|
async getPermissions(win) {
|
||||||
win
|
|
||||||
) {
|
|
||||||
// Keep prompting the user until they pick a file that grants us access,
|
// Keep prompting the user until they pick a file that grants us access,
|
||||||
// or they cancel out of the file open panel.
|
// or they cancel out of the file open panel.
|
||||||
while (!(await this.hasPermissions())) {
|
while (!(await this.hasPermissions())) {
|
||||||
@@ -444,13 +452,10 @@ SafariProfileMigrator.prototype.getPermissions = async function SM_getPermission
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(
|
get mainPreferencesPropertyList() {
|
||||||
SafariProfileMigrator.prototype,
|
|
||||||
"mainPreferencesPropertyList",
|
|
||||||
{
|
|
||||||
get: function get_mainPreferencesPropertyList() {
|
|
||||||
if (this._mainPreferencesPropertyList === undefined) {
|
if (this._mainPreferencesPropertyList === undefined) {
|
||||||
let file = FileUtils.getDir("UsrPrfs", [], false);
|
let file = FileUtils.getDir("UsrPrfs", [], false);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
@@ -466,13 +471,5 @@ Object.defineProperty(
|
|||||||
return this._mainPreferencesPropertyList;
|
return this._mainPreferencesPropertyList;
|
||||||
}
|
}
|
||||||
return this._mainPreferencesPropertyList;
|
return this._mainPreferencesPropertyList;
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
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}"
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ EXTRA_JS_MODULES += [
|
|||||||
"ChromeProfileMigrator.sys.mjs",
|
"ChromeProfileMigrator.sys.mjs",
|
||||||
"FirefoxProfileMigrator.sys.mjs",
|
"FirefoxProfileMigrator.sys.mjs",
|
||||||
"MigrationUtils.sys.mjs",
|
"MigrationUtils.sys.mjs",
|
||||||
|
"MigratorBase.sys.mjs",
|
||||||
"ProfileMigrator.sys.mjs",
|
"ProfileMigrator.sys.mjs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var { MigrationUtils, MigratorPrototype } = ChromeUtils.importESModule(
|
var { MigrationUtils } = ChromeUtils.importESModule(
|
||||||
"resource:///modules/MigrationUtils.sys.mjs"
|
"resource:///modules/MigrationUtils.sys.mjs"
|
||||||
);
|
);
|
||||||
var { LoginHelper } = ChromeUtils.import(
|
var { LoginHelper } = ChromeUtils.import(
|
||||||
@@ -42,7 +42,7 @@ updateAppInfo();
|
|||||||
/**
|
/**
|
||||||
* Migrates the requested resource and waits for the migration to be complete.
|
* Migrates the requested resource and waits for the migration to be complete.
|
||||||
*
|
*
|
||||||
* @param {MigratorPrototype} migrator
|
* @param {MigratorBase} migrator
|
||||||
* The migrator being used to migrate the data.
|
* The migrator being used to migrate the data.
|
||||||
* @param {number} resourceType
|
* @param {number} resourceType
|
||||||
* This is a bitfield with bits from nsIBrowserProfileMigrator flipped to indicate what
|
* This is a bitfield with bits from nsIBrowserProfileMigrator flipped to indicate what
|
||||||
|
|||||||
Reference in New Issue
Block a user