forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			316 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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/. */
 | |
| 
 | |
| 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",
 | |
|   ]);
 | |
| });
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   InternalTestingProfileMigrator:
 | |
|     "resource:///modules/InternalTestingProfileMigrator.sys.mjs",
 | |
|   MigrationWizardConstants:
 | |
|     "chrome://browser/content/migration/migration-wizard-constants.mjs",
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * This class is responsible for communicating with MigrationUtils to do the
 | |
|  * actual heavy-lifting of any kinds of migration work, based on messages from
 | |
|  * the associated MigrationWizardChild.
 | |
|  */
 | |
| export class MigrationWizardParent extends JSWindowActorParent {
 | |
|   /**
 | |
|    * General message handler function for messages received from the
 | |
|    * associated MigrationWizardChild JSWindowActor.
 | |
|    *
 | |
|    * @param {ReceiveMessageArgument} message
 | |
|    *   The message received from the MigrationWizardChild.
 | |
|    * @returns {Promise}
 | |
|    */
 | |
|   async receiveMessage(message) {
 | |
|     // Some belt-and-suspenders here, mainly because the migration-wizard
 | |
|     // component can be embedded in less privileged content pages, so let's
 | |
|     // make sure that any messages from content are coming from the privileged
 | |
|     // about content process type.
 | |
|     if (
 | |
|       !this.browsingContext.currentWindowGlobal.isInProcess &&
 | |
|       this.browsingContext.currentRemoteType !=
 | |
|         E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
 | |
|     ) {
 | |
|       throw new Error(
 | |
|         "MigrationWizardParent: received message from the wrong content process type."
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     switch (message.name) {
 | |
|       case "GetAvailableMigrators": {
 | |
|         let availableMigrators = [];
 | |
|         for (const key of MigrationUtils.availableMigratorKeys) {
 | |
|           availableMigrators.push(this.#getMigratorAndProfiles(key));
 | |
|         }
 | |
|         // Wait for all getMigrator calls to resolve in parallel
 | |
|         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)
 | |
|           .sort((a, b) => {
 | |
|             return b.lastModifiedDate - a.lastModifiedDate;
 | |
|           });
 | |
|       }
 | |
| 
 | |
|       case "Migrate": {
 | |
|         await this.#doMigration(
 | |
|           message.data.key,
 | |
|           message.data.resourceTypes,
 | |
|           message.data.profile
 | |
|         );
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Calls into MigrationUtils to perform a migration given the parameters
 | |
|    * sent via the wizard.
 | |
|    *
 | |
|    * @param {string} migratorKey
 | |
|    *   The unique identification key for a migrator.
 | |
|    * @param {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.
 | |
|    * @param {object|null} profileObj
 | |
|    *   A description of the user profile that the migrator can import.
 | |
|    * @param {string} profileObj.id
 | |
|    *   A unique ID for the user profile.
 | |
|    * @param {string} profileObj.name
 | |
|    *   The display name for the user profile.
 | |
|    * @returns {Promise<undefined>}
 | |
|    *   Resolves once the Migration:Ended observer notification has fired.
 | |
|    */
 | |
|   async #doMigration(migratorKey, resourceTypes, profileObj) {
 | |
|     let migrator = await MigrationUtils.getMigrator(migratorKey);
 | |
|     let resourceTypesToMigrate = 0;
 | |
|     let progress = {};
 | |
| 
 | |
|     for (let resourceType of resourceTypes) {
 | |
|       resourceTypesToMigrate |= MigrationUtils.resourceTypes[resourceType];
 | |
|       progress[resourceType] = {
 | |
|         inProgress: true,
 | |
|         message: "",
 | |
|       };
 | |
|     }
 | |
| 
 | |
|     this.sendAsyncMessage("UpdateProgress", progress);
 | |
| 
 | |
|     try {
 | |
|       await migrator.migrate(
 | |
|         resourceTypesToMigrate,
 | |
|         false,
 | |
|         profileObj,
 | |
|         async resourceTypeNum => {
 | |
|           // Unfortunately, MigratorBase hands us the the numeric value of the
 | |
|           // MigrationUtils.resourceType for this callback. For now, we'll just
 | |
|           // do a look-up to map it to the right constant.
 | |
|           let foundResourceTypeName;
 | |
|           for (let resourceTypeName in MigrationUtils.resourceTypes) {
 | |
|             if (
 | |
|               MigrationUtils.resourceTypes[resourceTypeName] == resourceTypeNum
 | |
|             ) {
 | |
|               foundResourceTypeName = resourceTypeName;
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           if (!foundResourceTypeName) {
 | |
|             console.error(
 | |
|               "Could not find a resource type for value: ",
 | |
|               resourceTypeNum
 | |
|             );
 | |
|           } else {
 | |
|             // For now, we ignore errors in migration, and simply display
 | |
|             // the success state.
 | |
|             progress[foundResourceTypeName] = {
 | |
|               inProgress: false,
 | |
|               message: await this.#getStringForImportQuantity(
 | |
|                 foundResourceTypeName
 | |
|               ),
 | |
|             };
 | |
|             this.sendAsyncMessage("UpdateProgress", progress);
 | |
|           }
 | |
|         }
 | |
|       );
 | |
|     } catch (e) {
 | |
|       console.error(e);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @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?.enabled) {
 | |
|         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 lastModifiedDate = await migrator.getLastUsedDate();
 | |
|     let availableResourceTypes = [];
 | |
| 
 | |
|     for (let resourceType in MigrationUtils.resourceTypes) {
 | |
|       if (profileMigrationData & MigrationUtils.resourceTypes[resourceType]) {
 | |
|         availableResourceTypes.push(resourceType);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     let displayName;
 | |
| 
 | |
|     if (migrator.constructor.key == lazy.InternalTestingProfileMigrator.key) {
 | |
|       // In the case of the InternalTestingProfileMigrator, which is never seen
 | |
|       // by users outside of testing, we don't make our localization community
 | |
|       // localize it's display name, and just display the ID instead.
 | |
|       displayName = migrator.constructor.displayNameL10nID;
 | |
|     } else {
 | |
|       displayName = await lazy.gFluentStrings.formatValue(
 | |
|         migrator.constructor.displayNameL10nID
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|       key: migrator.constructor.key,
 | |
|       displayName,
 | |
|       resourceTypes: availableResourceTypes,
 | |
|       profile: profileObj,
 | |
|       lastModifiedDate,
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the "success" string for a particular resource type after
 | |
|    * migration has completed.
 | |
|    *
 | |
|    * @param {string} resourceTypeStr
 | |
|    *   A string mapping to one of the key values of
 | |
|    *   MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.
 | |
|    * @returns {Promise<string>}
 | |
|    *   The success string for the resource type after migration has completed.
 | |
|    */
 | |
|   #getStringForImportQuantity(resourceTypeStr) {
 | |
|     switch (resourceTypeStr) {
 | |
|       case lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.BOOKMARKS: {
 | |
|         let quantity = MigrationUtils.getImportedCount("bookmarks");
 | |
|         return lazy.gFluentStrings.formatValue(
 | |
|           "migration-wizard-progress-success-bookmarks",
 | |
|           {
 | |
|             quantity,
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       case lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.HISTORY: {
 | |
|         let quantity = MigrationUtils.getImportedCount("history");
 | |
|         return lazy.gFluentStrings.formatValue(
 | |
|           "migration-wizard-progress-success-history",
 | |
|           {
 | |
|             quantity,
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       case lazy.MigrationWizardConstants.DISPLAYED_RESOURCE_TYPES.PASSWORDS: {
 | |
|         let quantity = MigrationUtils.getImportedCount("logins");
 | |
|         return lazy.gFluentStrings.formatValue(
 | |
|           "migration-wizard-progress-success-passwords",
 | |
|           {
 | |
|             quantity,
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       default: {
 | |
|         return "";
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | 
