Bug 1811935 - Adjust visibility of the resource types available for each migrator / profile pair in the new migration wizard. r=Gijs

This causes the MigrationWizardChild to request the full collection of available
migrators and user profiles, and then based on which resourceTypes those migrators
and user profiles have available, changes the visibility of the checkboxes in the
selection page of the new MigrationWizard component.

Differential Revision: https://phabricator.services.mozilla.com/D167617
This commit is contained in:
Mike Conley 2023-01-25 12:53:31 +00:00
Родитель c216ddfc4f
Коммит 828431224a
12 изменённых файлов: 402 добавлений и 49 удалений

Просмотреть файл

@ -77,6 +77,10 @@ export class ChromeProfileMigrator extends MigratorBase {
return "chrome";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chrome";
}
get _chromeUserDataPathSuffix() {
return "Chrome";
}
@ -615,6 +619,10 @@ export class ChromiumProfileMigrator extends ChromeProfileMigrator {
return "chromium";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chromium";
}
_chromeUserDataPathSuffix = "Chromium";
_keychainServiceName = "Chromium Safe Storage";
_keychainAccountName = "Chromium";
@ -629,6 +637,10 @@ export class CanaryProfileMigrator extends ChromeProfileMigrator {
return "canary";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-canary";
}
get _chromeUserDataPathSuffix() {
return "Canary";
}
@ -650,6 +662,10 @@ export class ChromeDevMigrator extends ChromeProfileMigrator {
return "chrome-dev";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chrome-dev";
}
_chromeUserDataPathSuffix = "Chrome Dev";
_keychainServiceName = "Chromium Safe Storage";
_keychainAccountName = "Chromium";
@ -663,6 +679,10 @@ export class ChromeBetaMigrator extends ChromeProfileMigrator {
return "chrome-beta";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chrome-beta";
}
_chromeUserDataPathSuffix = "Chrome Beta";
_keychainServiceName = "Chromium Safe Storage";
_keychainAccountName = "Chromium";
@ -676,6 +696,10 @@ export class BraveProfileMigrator extends ChromeProfileMigrator {
return "brave";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-brave";
}
_chromeUserDataPathSuffix = "Brave";
_keychainServiceName = "Brave Browser Safe Storage";
_keychainAccountName = "Brave Browser";
@ -689,6 +713,10 @@ export class ChromiumEdgeMigrator extends ChromeProfileMigrator {
return "chromium-edge";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chromium-edge";
}
_chromeUserDataPathSuffix = "Edge";
_keychainServiceName = "Microsoft Edge Safe Storage";
_keychainAccountName = "Microsoft Edge";
@ -702,6 +730,10 @@ export class ChromiumEdgeBetaMigrator extends ChromeProfileMigrator {
return "chromium-edge-beta";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chromium-edge-beta";
}
_chromeUserDataPathSuffix = "Edge Beta";
_keychainServiceName = "Microsoft Edge Safe Storage";
_keychainAccountName = "Microsoft Edge";
@ -715,6 +747,10 @@ export class Chromium360seMigrator extends ChromeProfileMigrator {
return "chromium-360se";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-chromium-360se";
}
_chromeUserDataPathSuffix = "360 SE";
_keychainServiceName = "Microsoft Edge Safe Storage";
_keychainAccountName = "Microsoft Edge";
@ -728,6 +764,10 @@ export class OperaProfileMigrator extends ChromeProfileMigrator {
return "opera";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-opera";
}
_chromeUserDataPathSuffix = "Opera";
_keychainServiceName = "Opera Safe Storage";
_keychainAccountName = "Opera";
@ -745,6 +785,10 @@ export class OperaGXProfileMigrator extends ChromeProfileMigrator {
return "opera-gx";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-opera-gx";
}
_chromeUserDataPathSuffix = "Opera GX";
_keychainServiceName = "Opera Safe Storage";
_keychainAccountName = "Opera";
@ -762,6 +806,10 @@ export class VivaldiProfileMigrator extends ChromeProfileMigrator {
return "vivaldi";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-vivaldi";
}
_chromeUserDataPathSuffix = "Vivaldi";
_keychainServiceName = "Vivaldi Safe Storage";
_keychainAccountName = "Vivaldi";

Просмотреть файл

@ -472,6 +472,10 @@ export class EdgeProfileMigrator extends MigratorBase {
return "edge";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-edge-legacy";
}
getBookmarksMigratorForTesting(dbOverride) {
return new EdgeBookmarksMigrator(dbOverride);
}

Просмотреть файл

@ -37,6 +37,10 @@ export class FirefoxProfileMigrator extends MigratorBase {
return "firefox";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-firefox";
}
_getAllProfiles() {
let allProfiles = new Map();
let profileService = Cc[

Просмотреть файл

@ -355,6 +355,10 @@ export class IEProfileMigrator extends MigratorBase {
return "ie";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-ie";
}
getResources() {
let resources = [
MSMigrationUtils.getBookmarksMigrator(),

Просмотреть файл

@ -4,6 +4,16 @@
import { MigrationUtils } from "resource:///modules/MigrationUtils.sys.mjs";
import { E10SUtils } from "resource://gre/modules/E10SUtils.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "gFluentStrings", function() {
return new Localization([
"branding/brand.ftl",
"locales-preview/migrationWizard.ftl",
]);
});
/**
* This class is responsible for communicating with MigrationUtils to do the
@ -35,26 +45,116 @@ export class MigrationWizardParent extends JSWindowActorParent {
}
if (message.name == "GetAvailableMigrators") {
let availableMigrators = new Map();
let availableMigrators = [];
for (const key of MigrationUtils.availableMigratorKeys) {
try {
let migratorPromise = MigrationUtils.getMigrator(key).catch(
console.error
);
if (migratorPromise) {
availableMigrators.set(key, migratorPromise);
}
} catch (e) {
console.error(`Could not get migrator with key ${key}`);
}
availableMigrators.push(this.#getMigratorAndProfiles(key));
}
// Wait for all getMigrator calls to resolve in parallel
await Promise.all(availableMigrators.values());
// ...and then filter out any that resolved to null.
return Array.from(availableMigrators.keys()).filter(key => {
return availableMigrators.get(key);
});
let results = await Promise.all(availableMigrators);
// Each migrator might give us a single MigratorProfileInstance,
// or an Array of them, so we flatten them out and filter out
// any that ended up going wrong and returning null from the
// #getMigratorAndProfiles call.
return results.flat().filter(result => result);
}
return null;
}
/**
* @typedef {object} MigratorProfileInstance
* An object that describes a single user profile (or the default
* user profile) for a particular migrator.
* @property {string} key
* The unique identification key for a migrator.
* @property {string} displayName
* The display name for the migrator that will be shown to the user
* in the wizard.
* @property {string[]} resourceTypes
* An array of strings, where each string represents a resource type
* that can be imported for this migrator and profile. The strings
* should be one of the key values of
* MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
*
* Example: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"]
* @property {object|null} profile
* A description of the user profile that the migrator can import.
* @property {string} profile.id
* A unique ID for the user profile.
* @property {string} profile.name
* The display name for the user profile.
*/
/**
* Asynchronously fetches a migrator for a particular key, and then
* also gets any user profiles that exist on for that migrator. Resolves
* to null if something goes wrong getting information about the migrator
* or any of the user profiles.
*
* @param {string} key
* The unique identification key for a migrator.
* @returns {Promise<MigratorProfileInstance[]|null>}
*/
async #getMigratorAndProfiles(key) {
try {
let migrator = await MigrationUtils.getMigrator(key);
if (!migrator) {
return null;
}
let sourceProfiles = await migrator.getSourceProfiles();
if (Array.isArray(sourceProfiles)) {
if (!sourceProfiles.length) {
return null;
}
let result = [];
for (let profile of sourceProfiles) {
result.push(
await this.#serializeMigratorAndProfile(migrator, profile)
);
}
return result;
}
return this.#serializeMigratorAndProfile(migrator, sourceProfiles);
} catch (e) {
console.error(`Could not get migrator with key ${key}`, e);
}
return null;
}
/**
* Asynchronously fetches information about what resource types can be
* migrated for a particular migrator and user profile, and then packages
* the migrator, user profile data, and resource type data into an object
* that can be sent down to the MigrationWizardChild.
*
* @param {MigratorBase} migrator
* A migrator subclass of MigratorBase.
* @param {object|null} profileObj
* The user profile object representing the profile to get information
* about. This object is usually gotten by calling getSourceProfiles on
* the migrator.
* @returns {Promise<MigratorProfileInstance>}
*/
async #serializeMigratorAndProfile(migrator, profileObj) {
let profileMigrationData = await migrator.getMigrateData(profileObj);
let availableResourceTypes = [];
for (let resourceType in MigrationUtils.resourceTypes) {
if (profileMigrationData & MigrationUtils.resourceTypes[resourceType]) {
availableResourceTypes.push(resourceType);
}
}
return {
key: migrator.constructor.key,
displayName: await lazy.gFluentStrings.formatValues([
{
id: migrator.constructor.displayNameL10nID,
},
]),
resourceTypes: availableResourceTypes,
profile: profileObj,
};
}
}

Просмотреть файл

@ -51,10 +51,20 @@ export class MigratorBase {
* migrator, for example "firefox", "chrome", "opera-gx". This key is what
* is used as an identifier when calling MigrationUtils.getMigrator.
*
* @type {boolean}
* @type {string}
*/
static get key() {
throw new Error("MigratorBase must be overridden.");
throw new Error("MigratorBase.key must be overridden.");
}
/**
* This must be overridden to return a Fluent string ID mapping to the display
* name for this migrator. These strings should be defined in migrationWizard.ftl.
*
* @type {string}
*/
static get displayNameL10nID() {
throw new Error("MigratorBase.displayNameL10nID must be overridden.");
}
/**

Просмотреть файл

@ -350,6 +350,10 @@ export class SafariProfileMigrator extends MigratorBase {
return "safari";
}
static get displayNameL10nID() {
return "migration-wizard-migrator-display-name-safari";
}
getResources() {
let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
if (!profileDir.exists()) {

Просмотреть файл

@ -17,6 +17,7 @@ export class MigrationWizard extends HTMLElement {
#deck = null;
#browserProfileSelector = null;
#resourceTypeList = null;
#shadowRoot = null;
static get markup() {
@ -29,18 +30,18 @@ export class MigrationWizard extends HTMLElement {
<h3 data-l10n-id="migration-wizard-selection-header"></h3>
<select id="browser-profile-selector">
</select>
<fieldset>
<label for="bookmarks">
<input type="checkbox" id="bookmarks"/><span data-l10n-id="migration-bookmarks-option-label"></span>
<fieldset id="resource-type-list">
<label id="bookmarks">
<input type="checkbox"/><span data-l10n-id="migration-bookmarks-option-label"></span>
</label>
<label for="logins-and-passwords">
<input type="checkbox" id="logins-and-passwords"/><span data-l10n-id="migration-logins-and-passwords-option-label"></span>
<label id="logins-and-passwords">
<input type="checkbox"/><span data-l10n-id="migration-logins-and-passwords-option-label"></span>
</label>
<label for="history">
<input type="checkbox" id="history"/><span data-l10n-id="migration-history-option-label"></span>
<label id="history">
<input type="checkbox"/><span data-l10n-id="migration-history-option-label"></span>
</label>
<label for="form-autofill">
<input type="checkbox" id="form-autofill"/><span data-l10n-id="migration-form-autofill-option-label"></span>
<label id="form-autofill">
<input type="checkbox"/><span data-l10n-id="migration-form-autofill-option-label"></span>
</label>
</fieldset>
<moz-button-group class="buttons">
@ -128,6 +129,8 @@ export class MigrationWizard extends HTMLElement {
button.addEventListener("click", this);
}
this.#browserProfileSelector.addEventListener("change", this);
this.#resourceTypeList = shadow.querySelector("#resource-type-list");
this.#shadowRoot = shadow;
}
@ -164,6 +167,30 @@ export class MigrationWizard extends HTMLElement {
}
}
/**
* Reacts to changes to the browser / profile selector dropdown. This
* should update the list of resource types to match what's supported
* by the selected migrator and profile.
*/
#onBrowserProfileSelectionChanged() {
let resourceTypes = this.#browserProfileSelector.selectedOptions[0]
.resourceTypes;
for (let child of this.#resourceTypeList.children) {
child.hidden = true;
}
for (let resourceType of resourceTypes) {
let resourceID =
MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES[resourceType];
let resourceLabel = this.#resourceTypeList.querySelector(
`#${resourceID}`
);
if (resourceLabel) {
resourceLabel.hidden = false;
}
}
}
/**
* Called when showing the browser/profile selection page of the wizard.
*
@ -176,12 +203,37 @@ export class MigrationWizard extends HTMLElement {
#onShowingSelection(state) {
this.#browserProfileSelector.textContent = "";
for (let migratorKey of state.migrators) {
for (let migrator of state.migrators) {
let opt = document.createElement("option");
opt.value = migratorKey;
opt.textContent = migratorKey;
opt.value = migrator.key;
opt.profile = migrator.profile;
opt.resourceTypes = migrator.resourceTypes;
if (migrator.profile) {
document.l10n.setAttributes(
opt,
"migration-wizard-selection-option-with-profile",
{
sourceBrowser: migrator.displayName,
profileName: migrator.profile.name,
}
);
} else {
document.l10n.setAttributes(
opt,
"migration-wizard-selection-option-without-profile",
{
sourceBrowser: migrator.displayName,
}
);
}
this.#browserProfileSelector.appendChild(opt);
}
if (state.migrators.length) {
this.#onBrowserProfileSelectionChanged();
}
}
/**
@ -273,13 +325,21 @@ export class MigrationWizard extends HTMLElement {
}
handleEvent(event) {
if (
event.type == "click" &&
event.target.classList.contains("cancel-close")
) {
this.dispatchEvent(
new CustomEvent("MigrationWizard:Close", { bubbles: true })
);
switch (event.type) {
case "click": {
if (event.target.classList.contains("cancel-close")) {
this.dispatchEvent(
new CustomEvent("MigrationWizard:Close", { bubbles: true })
);
}
break;
}
case "change": {
if (event.target == this.#browserProfileSelector) {
this.#onBrowserProfileSelectionChanged();
}
break;
}
}
}
}

Просмотреть файл

@ -18,6 +18,21 @@
"resource://testing-common/BrowserTestUtils.sys.mjs"
);
const MIGRATOR_PROFILE_INSTANCES = [
{
key: "some-browser-0",
displayName: "Some Browser 0",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "person-2", name: "Person 2" },
},
{
key: "some-browser-1",
displayName: "Some Browser 1",
resourceTypes: ["HISTORY", "BOOKMARKS"],
profile: null,
},
];
let gWiz = null;
let gShadowRoot = null;
let gDeck = null;
@ -69,13 +84,34 @@
add_task(async function test_selection() {
gWiz.setState({
page: MigrationWizardConstants.PAGES.SELECTION,
migrators: ["some-browser-0", "some-browser-1"],
migrators: MIGRATOR_PROFILE_INSTANCES,
});
let selector = gShadowRoot.querySelector("#browser-profile-selector");
is(selector.childElementCount, 2, "Should have two child elements");
is(selector.children[0].value, "some-browser-0");
is(selector.children[1].value, "some-browser-1");
let resourceTypeList = gShadowRoot.querySelector("#resource-type-list");
// Test that the resource type checkboxes are shown or hidden depending on
// which resourceTypes are included with the MigratorProfileInstance.
for (let migratorInstance of MIGRATOR_PROFILE_INSTANCES) {
selector.value = migratorInstance.key;
// Apparently we have to dispatch our own "change" events for <select>
// dropdowns.
selector.dispatchEvent(new CustomEvent("change", { "bubbles" : true }));
for (let displayedResourceType in MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES) {
let resourceTypeID = MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES[displayedResourceType];
let node = resourceTypeList.querySelector(`#${resourceTypeID}`);
if (migratorInstance.resourceTypes.includes(displayedResourceType)) {
ok(!isHidden(node), `Selection for ${displayedResourceType} should be shown.`);
} else {
ok(isHidden(node), `Selection for ${displayedResourceType} should be hidden.`);
}
}
}
});
/**
@ -205,7 +241,7 @@
gWiz.toggleAttribute("dialog-mode", true);
gWiz.setState({
page: MigrationWizardConstants.PAGES.SELECTION,
migrators: ["some-browser-0", "some-browser-1"],
migrators: MIGRATOR_PROFILE_INSTANCES,
});
// For now, there's only a single .cancel-close button, so let's just test

Просмотреть файл

@ -16,15 +16,60 @@ export default {
};
const FAKE_BROWSER_LIST = [
"Google Chrome - Profile 1",
"Google Chrome - Profile 2",
"Internet Explorer",
"Edge",
"Brave",
"Safari",
"Vivaldi",
"Opera",
"Opera GX",
{
key: "chrome",
displayName: "Chrome",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
{
key: "chrome",
displayName: "Chrome",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "person-2", name: "Person 2" },
},
{
key: "ie",
displayName: "Microsoft Internet Explorer",
resourceTypes: ["HISTORY", "BOOKMARKS"],
profile: null,
},
{
key: "chromium-edge",
displayName: "Microsoft Edge",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
{
key: "brave",
displayName: "Brave",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
{
key: "safari",
displayName: "Safari",
resourceTypes: ["HISTORY", "PASSWORDS", "BOOKMARKS"],
profile: null,
},
{
key: "opera",
displayName: "Opera",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
{
key: "opera-gx",
displayName: "Opera GX",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
{
key: "vivaldi",
displayName: "Vivaldi",
resourceTypes: ["HISTORY", "FORMDATA", "PASSWORDS", "BOOKMARKS"],
profile: { id: "Default", name: "Default" },
},
];
const Template = ({ state, dialogMode }) => {

Просмотреть файл

@ -4,6 +4,44 @@
migration-wizard-selection-header = Import Data from Another Browser
# Shown in the new migration wizard's dropdown selector for choosing the browser
# to import from. This variant is shown when the selected browser doesn't support
# user profiles, and so we only show the browser name.
#
# Variables:
# $sourceBrowser (String): the name of the browser to import from.
migration-wizard-selection-option-without-profile = { $sourceBrowser }
# Shown in the new migration wizard's dropdown selector for choosing the browser
# and user profile to import from. This variant is shown when the selected browser
# supports user profiles.
#
# Variables:
# $sourceBrowser (String): the name of the browser to import from.
# $profileName (String): the name of the user profile to import from.
migration-wizard-selection-option-with-profile = { $sourceBrowser } — { $profileName }
# Each migrator is expected to include a display name string, and that display
# name string should have a key with "migration-wizard-migrator-display-name-"
# as a prefix followed by the unique identification key for the migrator.
migration-wizard-migrator-display-name-brave = Brave
migration-wizard-migrator-display-name-canary = Chrome Canary
migration-wizard-migrator-display-name-chrome = Chrome
migration-wizard-migrator-display-name-chrome-beta = Chrome Beta
migration-wizard-migrator-display-name-chrome-dev = Chrome Dev
migration-wizard-migrator-display-name-chromium = Chromium
migration-wizard-migrator-display-name-chromium-360se = 360 Secure Browser
migration-wizard-migrator-display-name-chromium-edge = Microsoft Edge
migration-wizard-migrator-display-name-chromium-edge-beta = Microsoft Edge Beta
migration-wizard-migrator-display-name-edge-legacy = Microsoft Edge Legacy
migration-wizard-migrator-display-name-firefox = { -brand-product-name }
migration-wizard-migrator-display-name-ie = Microsoft Internet Explorer
migration-wizard-migrator-display-name-opera = Opera
migration-wizard-migrator-display-name-opera-gx = Opera GX
migration-wizard-migrator-display-name-safari = Safari
migration-wizard-migrator-display-name-vivaldi = Vivaldi
migration-bookmarks-option-label = Bookmarks
migration-logins-and-passwords-option-label = Saved logins and passwords
migration-history-option-label = Browsing history

Просмотреть файл

@ -19,7 +19,7 @@ fieldset {
border: 0;
}
label {
label:not([hidden]) {
display: flex;
align-items: center;
}