Bug 1823537 - Use new migration wizard for startup and profile reset migrations. r=kpatenio,Gijs,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D180230
This commit is contained in:
Mike Conley 2023-06-09 17:39:51 +00:00
parent c57e85b6fa
commit cffc131623
17 changed files with 239 additions and 64 deletions

View File

@ -600,31 +600,6 @@ let JSWINDOWACTORS = {
messageManagerGroups: ["browsers"],
},
MigrationWizard: {
parent: {
esModuleURI: "resource:///actors/MigrationWizardParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/MigrationWizardChild.sys.mjs",
events: {
"MigrationWizard:RequestState": { wantUntrusted: true },
"MigrationWizard:BeginMigration": { wantUntrusted: true },
"MigrationWizard:RequestSafariPermissions": { wantUntrusted: true },
"MigrationWizard:SelectSafariPasswordFile": { wantUntrusted: true },
},
},
includeChrome: true,
allFrames: true,
matches: [
"about:welcome",
"about:welcome?*",
"about:preferences",
"chrome://browser/content/migration/migration-dialog-window.html",
],
},
PageInfo: {
child: {
esModuleURI: "resource:///actors/PageInfoChild.sys.mjs",

View File

@ -28,7 +28,7 @@ module.exports = {
overrides: [
{
files: ["tests/browser/head.js", "tests/unit/head*.js"],
files: ["head*.js"],
rules: {
"no-unused-vars": [
"error",

View File

@ -130,6 +130,31 @@ class MigrationUtils {
"browser.migrate.history.maxAgeInDays",
180
);
ChromeUtils.registerWindowActor("MigrationWizard", {
parent: {
esModuleURI: "resource:///actors/MigrationWizardParent.sys.mjs",
},
child: {
esModuleURI: "resource:///actors/MigrationWizardChild.sys.mjs",
events: {
"MigrationWizard:RequestState": { wantUntrusted: true },
"MigrationWizard:BeginMigration": { wantUntrusted: true },
"MigrationWizard:RequestSafariPermissions": { wantUntrusted: true },
"MigrationWizard:SelectSafariPasswordFile": { wantUntrusted: true },
},
},
includeChrome: true,
allFrames: true,
matches: [
"about:welcome",
"about:welcome?*",
"about:preferences",
"chrome://browser/content/migration/migration-dialog-window.html",
],
});
}
resourceTypes = Object.freeze({
@ -565,11 +590,7 @@ class MigrationUtils {
*/
showMigrationWizard(aOpener, aOptions) {
if (
Services.prefs.getBoolPref(
"browser.migrate.content-modal.enabled",
false
) &&
!aOptions?.isStartupMigration
Services.prefs.getBoolPref("browser.migrate.content-modal.enabled", false)
) {
let entrypoint =
aOptions.entrypoint || this.MIGRATION_ENTRYPOINTS.UNKNOWN;
@ -577,17 +598,19 @@ class MigrationUtils {
.getHistogramById("FX_MIGRATION_ENTRY_POINT_CATEGORICAL")
.add(entrypoint);
let openStandaloneWindow = () => {
const FEATURES = "dialog,centerscreen,resizable=no";
const win = Services.ww.openWindow(
let openStandaloneWindow = blocking => {
let features = "dialog,centerscreen,resizable=no";
if (blocking) {
features += ",modal";
}
Services.ww.openWindow(
aOpener,
"chrome://browser/content/migration/migration-dialog-window.html",
"_blank",
FEATURES,
features,
{
onResize: () => {
win.sizeToContent();
},
options: aOptions,
}
);
@ -595,7 +618,7 @@ class MigrationUtils {
};
if (aOptions.isStartupMigration) {
openStandaloneWindow();
openStandaloneWindow(true /* blocking */);
return Promise.resolve();
}
@ -627,7 +650,7 @@ class MigrationUtils {
if (aboutWelcomeBehavior == "autoclose") {
return aOpener.openPreferences("general-migrate-autoclose");
} else if (aboutWelcomeBehavior == "standalone") {
openStandaloneWindow();
openStandaloneWindow(false /* blocking */);
return Promise.resolve();
}
}
@ -636,7 +659,7 @@ class MigrationUtils {
// If somehow we failed to open about:preferences, fall back to opening
// the top-level window.
openStandaloneWindow();
openStandaloneWindow(false /* blocking */);
return Promise.resolve();
}
// Legacy migration dialog

View File

@ -30,11 +30,12 @@ export class MigrationWizardChild extends JSWindowActorChild {
* @returns {Promise}
*/
async handleEvent(event) {
this.#wizardEl ??= event.target;
switch (event.type) {
case "MigrationWizard:RequestState": {
this.#sendTelemetryEvent("opened");
this.#wizardEl = event.target;
this.setComponentState({
page: MigrationWizardConstants.PAGES.LOADING,
});

View File

@ -17,6 +17,7 @@ XPCOMUtils.defineLazyGetter(lazy, "gFluentStrings", function () {
});
ChromeUtils.defineESModuleGetters(lazy, {
FirefoxProfileMigrator: "resource:///modules/FirefoxProfileMigrator.sys.mjs",
InternalTestingProfileMigrator:
"resource:///modules/InternalTestingProfileMigrator.sys.mjs",
MigrationWizardConstants:
@ -570,6 +571,10 @@ export class MigrationWizardParent extends JSWindowActorParent {
* The success string for the resource type after migration has completed.
*/
#getStringForImportQuantity(migratorKey, resourceTypeStr) {
if (migratorKey == lazy.FirefoxProfileMigrator.key) {
return "";
}
switch (resourceTypeStr) {
case lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS: {
let quantity = MigrationUtils.getImportedCount("bookmarks");

View File

@ -20,6 +20,11 @@
rel="stylesheet"
href="chrome://browser/skin/migration/migration-dialog-window.css"
/>
<script src="chrome://global/content/customElements.js"></script>
<script
src="chrome://browser/content/migration/migration-wizard-constants.mjs"
type="module"
></script>
<script
src="chrome://browser/content/migration/migration-wizard.mjs"
type="module"

View File

@ -4,6 +4,14 @@
"use strict";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
MigrationWizardConstants:
"chrome://browser/content/migration/migration-wizard-constants.mjs",
});
/**
* This file manages a MigrationWizard embedded in a dialog that runs
* in a top-level dialog window. It's main responsibility is to listen
@ -14,9 +22,6 @@
* this dialog.
*
* @param {object} window.arguments.0
* @param {Function} window.arguments.0.onResize
* A callback to resize the container of this document when the
* MigrationWizard resizes.
* @param {object} window.arguments.0.options
* A series of options for configuring the dialog. See
* MigrationUtils.showMigrationWizard for a description of this
@ -44,11 +49,8 @@ const MigrationDialog = {
args = args.wrappedJSObject;
}
// We have to inform the container of this document that the
// MigrationWizard has changed size in order for it to update
// its dimensions too.
let observer = new ResizeObserver(() => {
args.onResize();
window.sizeToContent();
});
observer.observe(this._wiz);
@ -56,7 +58,20 @@ const MigrationDialog = {
let panel = document.createXULElement("panel");
panel.appendChild(panelList);
this._wiz.appendChild(panel);
this._wiz.requestState();
customElements.whenDefined("migration-wizard").then(() => {
if (args.options?.skipSourceSelection) {
// This is an automigration for a profile refresh, so begin migration
// automatically once ready.
this.doProfileRefresh(
args.options.migratorKey,
args.options.migrator,
args.options.profileId
);
} else {
this._wiz.requestState();
}
});
},
handleEvent(event) {
@ -77,6 +92,30 @@ const MigrationDialog = {
}
}
},
async doProfileRefresh(migratorKey, migrator, profileId) {
let profile = { id: profileId };
let resourceTypeData = await migrator.getMigrateData(profile);
let resourceTypeStrs = [];
for (let type in lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
if (resourceTypeData & lazy.MigrationUtils.resourceTypes[type]) {
resourceTypeStrs.push(
lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES[type]
);
}
}
this._wiz.doAutoImport(migratorKey, profile, resourceTypeStrs);
this._wiz.addEventListener(
"MigrationWizard:DoneMigration",
() => {
setTimeout(() => {
window.close();
}, 5000);
},
{ once: true }
);
},
};
MigrationDialog.init();

View File

@ -48,7 +48,9 @@ export const MigrationWizardConstants = Object.freeze({
BOOKMARKS: "BOOKMARKS",
PAYMENT_METHODS: "PAYMENT_METHODS",
// We don't yet show OTHERDATA or SESSION resources.
COOKIES: "COOKIES",
SESSION: "SESSION",
OTHERDATA: "OTHERDATA",
}),
DISPLAYED_FILE_RESOURCE_TYPES: Object.freeze({
@ -63,6 +65,20 @@ export const MigrationWizardConstants = Object.freeze({
BOOKMARKS_FROM_FILE: "BOOKMARKS_FROM_FILE",
}),
/**
* Returns a mapping of a resource type to a string used to identify
* the associated resource group in the wizard via a data-resource-type
* attribute. The keys are for resource types that are only ever shown
* for profile resets.
*
* @type {Object<string, string>}
*/
PROFILE_RESET_ONLY_RESOURCE_TYPES: Object.freeze({
COOKIES: "COOKIES",
SESSION: "SESSION",
OTHERDATA: "OTHERDATA",
}),
/**
* The set of keys that maps to migrators that use the term "favorites"
* in the place of "bookmarks". This tends to be browsers from Microsoft.

View File

@ -130,6 +130,24 @@ export class MigrationWizard extends HTMLElement {
<span data-l10n-id="migration-payment-methods-option-label"></span>
<span class="success-text deemphasized-text">&nbsp;</span>
</div>
<div data-resource-type="COOKIES" class="resource-progress-group">
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
<span data-l10n-id="migration-cookies-option-label"></span>
<span class="success-text deemphasized-text">&nbsp;</span>
</div>
<div data-resource-type="SESSION" class="resource-progress-group">
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
<span data-l10n-id="migration-session-option-label"></span>
<span class="success-text deemphasized-text">&nbsp;</span>
</div>
<div data-resource-type="OTHERDATA" class="resource-progress-group">
<span class="progress-icon-parent"><span class="progress-icon" role="img"></span></span>
<span data-l10n-id="migration-otherdata-option-label"></span>
<span class="success-text deemphasized-text">&nbsp;</span>
</div>
</div>
<moz-button-group class="buttons" part="buttons">
<button class="cancel-close" data-l10n-id="migration-cancel-button-label" disabled></button>
@ -389,6 +407,7 @@ export class MigrationWizard extends HTMLElement {
// closely lines up with the edges of the selector button.
this.#browserProfileSelectorList.style.boxSizing = "border-box";
this.#browserProfileSelectorList.style.overflowY = "auto";
this.#browserProfileSelectorList.style.maxHeight = "100%";
}
/**
@ -786,6 +805,35 @@ export class MigrationWizard extends HTMLElement {
});
}
/**
* A public method for starting a migration without the user needing
* to choose a browser, profile or resource types. This is typically
* done only for doing a profile reset.
*
* @param {string} migratorKey
* The key associated with the migrator to use.
* @param {object|null} profile
* A representation of a browser profile. When not null, this is an
* object with a string "id" property, and a string "name" property.
* @param {string[]} resourceTypes
* An array of resource types that import should occur for. These
* strings should be from MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
*/
doAutoImport(migratorKey, profile, resourceTypes) {
let migrationEventDetail = this.#gatherMigrationEventDetails({
migratorKey,
profile,
resourceTypes,
});
this.dispatchEvent(
new CustomEvent("MigrationWizard:BeginMigration", {
bubbles: true,
detail: migrationEventDetail,
})
);
}
/**
* Takes the current state of the selections page and bundles them
* up into a MigrationWizard:BeginMigration event that can be handled
@ -826,9 +874,33 @@ export class MigrationWizard extends HTMLElement {
* and returns an object that can be used to begin migration via and event
* sent to the MigrationWizardChild.
*
* @param {object} [autoMigrationDetails=null]
* Provided iff an automatic migration is being invoked. In that case, the
* details are constructed from this object rather than the wizard DOM state.
* @param {string} autoMigrationDetails.migratorKey
* The key of the migrator to do automatic migration from.
* @param {object|null} autoMigrationDetails.profile
* A representation of a browser profile. When not null, this is an
* object with a string "id" property, and a string "name" property.
* @param {string[]} autoMigrationDetails.resourceTypes
* An array of resource types that import should occur for. These
* strings should be from MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
* @returns {MigrationDetails} details
*/
#gatherMigrationEventDetails() {
#gatherMigrationEventDetails(autoMigrationDetails) {
if (autoMigrationDetails?.migratorKey) {
let { migratorKey, profile, resourceTypes } = autoMigrationDetails;
return {
key: migratorKey,
type: MigrationWizardConstants.MIGRATOR_TYPES.BROWSER,
profile,
resourceTypes,
hasPermissions: true,
expandedDetails: this.#expandedDetails,
};
}
let panelItem = this.#browserProfileSelector.selectedPanelItem;
let key = panelItem.getAttribute("key");
let type = panelItem.getAttribute("type");

View File

@ -3,6 +3,8 @@ head = head.js
prefs =
browser.migrate.content-modal.enabled=true
browser.migrate.internal-testing.enabled=true
support-files =
../head-common.js
[browser_aboutwelcome_behavior.js]
[browser_dialog_cancel_close.js]

View File

@ -37,7 +37,7 @@ add_task(async function test_cancel_close() {
DIALOG_URL,
"_blank",
"dialog,centerscreen",
{ onResize: () => {} }
{}
);
await promiseWinLoaded;

View File

@ -107,7 +107,12 @@ add_task(async function test_successful_migrations() {
// Scenario 3: Several resource types are available, all are checked.
let allResourceTypeStrs = Object.values(
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES
);
).filter(resourceStr => {
return !MigrationWizardConstants.PROFILE_RESET_ONLY_RESOURCE_TYPES[
resourceStr
];
});
let allResourceTypes = allResourceTypeStrs.map(resourceTypeStr => {
return MigrationUtils.resourceTypes[resourceTypeStr];
});

View File

@ -3,12 +3,16 @@
"use strict";
/* import-globals-from ../head-common.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/browser/components/migration/tests/browser/head-common.js",
this
);
const { sinon } = ChromeUtils.importESModule(
"resource://testing-common/Sinon.sys.mjs"
);
const { MigrationWizardConstants } = ChromeUtils.importESModule(
"chrome://browser/content/migration/migration-wizard-constants.mjs"
);
const { InternalTestingProfileMigrator } = ChromeUtils.importESModule(
"resource:///modules/InternalTestingProfileMigrator.sys.mjs"
);
@ -242,7 +246,7 @@ async function selectResourceTypesAndStartMigration(
// And then check the right checkboxes for the resource types.
let resourceTypeList = shadow.querySelector("#resource-type-list");
for (let resourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
for (let resourceType of getChoosableResourceTypes()) {
let node = resourceTypeList.querySelector(
`label[data-resource-type="${resourceType}"]`
);

View File

@ -1,4 +1,6 @@
[DEFAULT]
skip-if = os == 'android'
support-files =
../head-common.js
[test_migration_wizard.html]

View File

@ -5,6 +5,7 @@
<title>Basic tests for the Migration Wizard component</title>
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script src="head-common.js"></script>
<script
src="chrome://browser/content/migration/migration-wizard.mjs"
type="module"
@ -14,11 +15,9 @@
href="chrome://mochikit/content/tests/SimpleTest/test.css"
/>
<script>
"use strict";
/* import-globals-from ../head-common.js */
const { MigrationWizardConstants } = ChromeUtils.importESModule(
"chrome://browser/content/migration/migration-wizard-constants.mjs"
);
"use strict";
const { BrowserTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/BrowserTestUtils.sys.mjs"
@ -179,7 +178,7 @@
is(profileName.textContent, "");
}
for (let resourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
for (let resourceType of getChoosableResourceTypes()) {
let node = resourceTypeList.querySelector(
`label[data-resource-type="${resourceType}"]`
);
@ -267,7 +266,7 @@
let migratorInstance = MIGRATOR_PROFILE_INSTANCES[i];
let panelItem = panelItems[i];
panelItem.click();
for (let resourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
for (let resourceType of getChoosableResourceTypes()) {
let node = gShadowRoot.querySelector(
`#resource-type-list label[data-resource-type="${resourceType}"]`
);

View File

@ -0,0 +1,24 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { MigrationWizardConstants } = ChromeUtils.importESModule(
"chrome://browser/content/migration/migration-wizard-constants.mjs"
);
/**
* Returns the constant strings from MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES
* that aren't also part of MigrationWizardConstants.PROFILE_RESET_ONLY_RESOURCE_TYPES.
*
* This is the set of resources that the user can actually choose to migrate via
* checkboxes.
*
* @returns {string[]}
*/
function getChoosableResourceTypes() {
return Object.keys(MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES).filter(
resourceType =>
!MigrationWizardConstants.PROFILE_RESET_ONLY_RESOURCE_TYPES[resourceType]
);
}

View File

@ -64,6 +64,9 @@ migration-logins-and-passwords-option-label = Saved logins and passwords
migration-history-option-label = Browsing history
migration-form-autofill-option-label = Form autofill data
migration-payment-methods-option-label = Payment methods
migration-cookies-option-label = Cookies
migration-session-option-label = Windows and tabs
migration-otherdata-option-label = Other data
migration-passwords-from-file-progress-header = Import Passwords File
migration-passwords-from-file-success-header = Passwords Imported Successfully