michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"]; michael@0: michael@0: const Cu = Components.utils; michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: michael@0: const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks"; michael@0: const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks"; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Dict", michael@0: "resource://gre/modules/Dict.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", michael@0: "resource://gre/modules/PlacesUtils.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", michael@0: "resource://gre/modules/NetUtil.jsm"); michael@0: XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", michael@0: "resource://gre/modules/BookmarkHTMLUtils.jsm"); michael@0: michael@0: let gMigrators = null; michael@0: let gProfileStartup = null; michael@0: let gMigrationBundle = null; michael@0: michael@0: function getMigrationBundle() { michael@0: if (!gMigrationBundle) { michael@0: gMigrationBundle = Services.strings.createBundle( michael@0: "chrome://browser/locale/migration/migration.properties"); michael@0: } michael@0: return gMigrationBundle; michael@0: } michael@0: michael@0: /** michael@0: * Figure out what is the default browser, and if there is a migrator michael@0: * for it, return that migrator's internal name. michael@0: * For the time being, the "internal name" of a migraotr is its contract-id michael@0: * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie), michael@0: * but it will soon be exposed properly. michael@0: */ michael@0: function getMigratorKeyForDefaultBrowser() { michael@0: // Don't map Firefox to the Firefox migrator, because we don't michael@0: // expect it to ever show up as an option in the wizard. michael@0: // We may want to revise this if/when we use separate profiles michael@0: // for each Firefox-update channel. michael@0: const APP_DESC_TO_KEY = { michael@0: "Internet Explorer": "ie", michael@0: "Safari": "safari", michael@0: "Google Chrome": "chrome", // Windows, Linux michael@0: "Chrome": "chrome", // OS X michael@0: }; michael@0: michael@0: let browserDesc = ""; michael@0: try { michael@0: let browserDesc = michael@0: Cc["@mozilla.org/uriloader/external-protocol-service;1"]. michael@0: getService(Ci.nsIExternalProtocolService). michael@0: getApplicationDescription("http"); michael@0: return APP_DESC_TO_KEY[browserDesc] || ""; michael@0: } michael@0: catch(ex) { michael@0: Cu.reportError("Could not detect default browser: " + ex); michael@0: } michael@0: return ""; michael@0: } michael@0: michael@0: /** michael@0: * Shared prototype for migrators, implementing nsIBrowserProfileMigrator. michael@0: * michael@0: * To implement a migrator: michael@0: * 1. Import this module. michael@0: * 2. Create the prototype for the migrator, extending MigratorPrototype. michael@0: * Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype); michael@0: * 3. Set classDescription, contractID and classID for your migrator, and set michael@0: * NSGetFactory appropriately. michael@0: * 4. If the migrator supports multiple profiles, override the sourceProfiles michael@0: * Here we default for single-profile migrator. michael@0: * 5. Implement getResources(aProfile) (see below). michael@0: * 6. If the migrator supports reading the home page of the source browser, michael@0: * override |sourceHomePageURL| getter. michael@0: * 7. For startup-only migrators, override |startupOnlyMigrator|. michael@0: */ michael@0: this.MigratorPrototype = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]), michael@0: michael@0: /** michael@0: * OVERRIDE IF AND ONLY IF the source supports multiple profiles. michael@0: * michael@0: * Returns array of profiles (by names) from which data may be imported. michael@0: * michael@0: * Only profiles from which data can be imported should be listed. Otherwise michael@0: * the behavior of the migration wizard isn't well-defined. michael@0: * michael@0: * For a single-profile source (e.g. safari, ie), this returns null, michael@0: * and not an empty array. That is the default implementation. michael@0: */ michael@0: get sourceProfiles() null, michael@0: michael@0: /** michael@0: * MUST BE OVERRIDDEN. michael@0: * michael@0: * Returns an array of "migration resources" objects for the given profile, michael@0: * or for the "default" profile, if the migrator does not support multiple michael@0: * profiles. michael@0: * michael@0: * Each migration resource should provide: michael@0: * - a |type| getter, retunring any of the migration types (see michael@0: * nsIBrowserProfileMigrator). michael@0: * michael@0: * - a |migrate| method, taking a single argument, aCallback(bool success), michael@0: * for migrating the data for this resource. It may do its job michael@0: * synchronously or asynchronously. Either way, it must call michael@0: * aCallback(bool aSuccess) when it's done. In the case of an exception michael@0: * thrown from |migrate|, it's taken as if aCallback(false) is called. michael@0: * michael@0: * Note: In the case of a simple asynchronous implementation, you may find michael@0: * MigrationUtils.wrapMigrateFunction handy for handling aCallback easily. michael@0: * michael@0: * For each migration type listed in nsIBrowserProfileMigrator, multiple michael@0: * migration resources may be provided. This practice is useful when the michael@0: * data for a certain migration type is independently stored in few michael@0: * locations. For example, the mac version of Safari stores its "reading list" michael@0: * bookmarks in a separate property list. michael@0: * michael@0: * Note that the importation of a particular migration type is reported as michael@0: * successful if _any_ of its resources succeeded to import (that is, called, michael@0: * |aCallback(true)|). However, completion-status for a particular migration michael@0: * type is reported to the UI only once all of its migrators have called michael@0: * aCallback. michael@0: * michael@0: * @note The returned array should only include resources from which data michael@0: * can be imported. So, for example, before adding a resource for the michael@0: * BOOKMARKS migration type, you should check if you should check that the michael@0: * bookmarks file exists. michael@0: * michael@0: * @param aProfile michael@0: * The profile from which data may be imported, or an empty string michael@0: * in the case of a single-profile migrator. michael@0: * In the case of multiple-profiles migrator, it is guaranteed that michael@0: * aProfile is a value returned by the sourceProfiles getter (see michael@0: * above). michael@0: */ michael@0: getResources: function MP_getResources(aProfile) { michael@0: throw new Error("getResources must be overridden"); michael@0: }, michael@0: michael@0: /** michael@0: * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now, michael@0: * that is just the Firefox migrator, see bug 737381). Default: false. michael@0: * michael@0: * Startup-only migrators are different in two ways: michael@0: * - they may only be used during startup. michael@0: * - the user-profile is half baked during migration. The folder exists, michael@0: * but it's only accessible through MigrationUtils.profileStartup. michael@0: * The migrator can call MigrationUtils.profileStartup.doStartup michael@0: * at any point in order to initialize the profile. michael@0: */ michael@0: get startupOnlyMigrator() false, michael@0: michael@0: /** michael@0: * OVERRIDE IF AND ONLY IF your migrator supports importing the homepage. michael@0: * @see nsIBrowserProfileMigrator michael@0: */ michael@0: get sourceHomePageURL() "", michael@0: michael@0: /** michael@0: * DO NOT OVERRIDE - After deCOMing migration, the UI will just call michael@0: * getResources. michael@0: * michael@0: * @see nsIBrowserProfileMigrator michael@0: */ michael@0: getMigrateData: function MP_getMigrateData(aProfile) { michael@0: let types = [r.type for each (r in this._getMaybeCachedResources(aProfile))]; michael@0: return types.reduce(function(a, b) a |= b, 0); michael@0: }, michael@0: michael@0: /** michael@0: * DO NOT OVERRIDE - After deCOMing migration, the UI will just call michael@0: * migrate for each resource. michael@0: * michael@0: * @see nsIBrowserProfileMigrator michael@0: */ michael@0: migrate: function MP_migrate(aItems, aStartup, aProfile) { michael@0: let resources = this._getMaybeCachedResources(aProfile); michael@0: if (resources.length == 0) michael@0: throw new Error("migrate called for a non-existent source"); michael@0: michael@0: if (aItems != Ci.nsIBrowserProfileMigrator.ALL) michael@0: resources = [r for each (r in resources) if (aItems & r.type)]; michael@0: michael@0: // Called either directly or through the bookmarks import callback. michael@0: function doMigrate() { michael@0: // TODO: use Map (for the items) and Set (for the resources) michael@0: // once they are iterable. michael@0: let resourcesGroupedByItems = new Dict(); michael@0: resources.forEach(function(resource) { michael@0: if (resourcesGroupedByItems.has(resource.type)) michael@0: resourcesGroupedByItems.get(resource.type).push(resource); michael@0: else michael@0: resourcesGroupedByItems.set(resource.type, [resource]); michael@0: }); michael@0: michael@0: if (resourcesGroupedByItems.count == 0) michael@0: throw new Error("No items to import"); michael@0: michael@0: let notify = function(aMsg, aItemType) { michael@0: Services.obs.notifyObservers(null, aMsg, aItemType); michael@0: } michael@0: michael@0: notify("Migration:Started"); michael@0: resourcesGroupedByItems.listkeys().forEach(function(migrationType) { michael@0: let migrationTypeA = migrationType; michael@0: let itemResources = resourcesGroupedByItems.get(migrationType); michael@0: notify("Migration:ItemBeforeMigrate", migrationType); michael@0: michael@0: let itemSuccess = false; michael@0: itemResources.forEach(function(resource) { michael@0: let resourceDone = function(aSuccess) { michael@0: let resourceIndex = itemResources.indexOf(resource); michael@0: if (resourceIndex != -1) { michael@0: itemResources.splice(resourceIndex, 1); michael@0: itemSuccess |= aSuccess; michael@0: if (itemResources.length == 0) { michael@0: resourcesGroupedByItems.del(migrationType); michael@0: notify(itemSuccess ? michael@0: "Migration:ItemAfterMigrate" : "Migration:ItemError", michael@0: migrationType); michael@0: if (resourcesGroupedByItems.count == 0) michael@0: notify("Migration:Ended"); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: Services.tm.mainThread.dispatch(function() { michael@0: // If migrate throws, an error occurred, and the callback michael@0: // (itemMayBeDone) might haven't been called. michael@0: try { michael@0: resource.migrate(resourceDone); michael@0: } michael@0: catch(ex) { michael@0: Cu.reportError(ex); michael@0: resourceDone(false); michael@0: } michael@0: }, Ci.nsIThread.DISPATCH_NORMAL); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) { michael@0: MigrationUtils.profileStartup.doStartup(); michael@0: michael@0: // If we're about to migrate bookmarks, first import the default bookmarks. michael@0: // Note We do not need to do so for the Firefox migrator michael@0: // (=startupOnlyMigrator), as it just copies over the places database michael@0: // from another profile. michael@0: const BOOKMARKS = MigrationUtils.resourceTypes.BOOKMARKS; michael@0: let migratingBookmarks = resources.some(function(r) r.type == BOOKMARKS); michael@0: if (migratingBookmarks) { michael@0: let browserGlue = Cc["@mozilla.org/browser/browserglue;1"]. michael@0: getService(Ci.nsIObserver); michael@0: browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, ""); michael@0: michael@0: // Note doMigrate doesn't care about the success of the import. michael@0: let onImportComplete = function() { michael@0: browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, ""); michael@0: doMigrate(); michael@0: }; michael@0: BookmarkHTMLUtils.importFromURL( michael@0: "resource:///defaults/profile/bookmarks.html", true).then( michael@0: onImportComplete, onImportComplete); michael@0: return; michael@0: } michael@0: } michael@0: doMigrate(); michael@0: }, michael@0: michael@0: /** michael@0: * DO NOT OVERRIDE - After deCOMing migration, this code michael@0: * won't be part of the migrator itself. michael@0: * michael@0: * @see nsIBrowserProfileMigrator michael@0: */ michael@0: get sourceExists() { michael@0: if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration) michael@0: return false; michael@0: michael@0: // For a single-profile source, check if any data is available. michael@0: // For multiple-profiles source, make sure that at least one michael@0: // profile is available. michael@0: let exists = false; michael@0: try { michael@0: let profiles = this.sourceProfiles; michael@0: if (!profiles) { michael@0: let resources = this._getMaybeCachedResources(""); michael@0: if (resources && resources.length > 0) michael@0: exists = true; michael@0: } michael@0: else { michael@0: exists = profiles.length > 0; michael@0: } michael@0: } michael@0: catch(ex) { michael@0: Cu.reportError(ex); michael@0: } michael@0: return exists; michael@0: }, michael@0: michael@0: /*** PRIVATE STUFF - DO NOT OVERRIDE ***/ michael@0: _getMaybeCachedResources: function PMB__getMaybeCachedResources(aProfile) { michael@0: if (this._resourcesByProfile) { michael@0: if (aProfile in this._resourcesByProfile) michael@0: return this._resourcesByProfile[aProfile]; michael@0: } michael@0: else { michael@0: this._resourcesByProfile = { }; michael@0: } michael@0: return this._resourcesByProfile[aProfile] = this.getResources(aProfile); michael@0: } michael@0: }; michael@0: michael@0: this.MigrationUtils = Object.freeze({ michael@0: resourceTypes: { michael@0: SETTINGS: Ci.nsIBrowserProfileMigrator.SETTINGS, michael@0: COOKIES: Ci.nsIBrowserProfileMigrator.COOKIES, michael@0: HISTORY: Ci.nsIBrowserProfileMigrator.HISTORY, michael@0: FORMDATA: Ci.nsIBrowserProfileMigrator.FORMDATA, michael@0: PASSWORDS: Ci.nsIBrowserProfileMigrator.PASSWORDS, michael@0: BOOKMARKS: Ci.nsIBrowserProfileMigrator.BOOKMARKS, michael@0: OTHERDATA: Ci.nsIBrowserProfileMigrator.OTHERDATA, michael@0: SESSION: Ci.nsIBrowserProfileMigrator.SESSION, michael@0: }, michael@0: michael@0: /** michael@0: * Helper for implementing simple asynchronous cases of migration resources' michael@0: * |migrate(aCallback)| (see MigratorPrototype). If your |migrate| method michael@0: * just waits for some file to be read, for example, and then migrates michael@0: * everything right away, you can wrap the async-function with this helper michael@0: * and not worry about notifying the callback. michael@0: * michael@0: * For example, instead of writing: michael@0: * setTimeout(function() { michael@0: * try { michael@0: * .... michael@0: * aCallback(true); michael@0: * } michael@0: * catch() { michael@0: * aCallback(false); michael@0: * } michael@0: * }, 0); michael@0: * michael@0: * You may write: michael@0: * setTimeout(MigrationUtils.wrapMigrateFunction(function() { michael@0: * if (importingFromMosaic) michael@0: * throw Cr.NS_ERROR_UNEXPECTED; michael@0: * }, aCallback), 0); michael@0: * michael@0: * ... and aCallback will be called with aSuccess=false when importing michael@0: * from Mosaic, or with aSuccess=true otherwise. michael@0: * michael@0: * @param aFunction michael@0: * the function that will be called sometime later. If aFunction michael@0: * throws when it's called, aCallback(false) is called, otherwise michael@0: * aCallback(true) is called. michael@0: * @param aCallback michael@0: * the callback function passed to |migrate|. michael@0: * @return the wrapped function. michael@0: */ michael@0: wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) { michael@0: return function() { michael@0: let success = false; michael@0: try { michael@0: aFunction.apply(null, arguments); michael@0: success = true; michael@0: } michael@0: catch(ex) { michael@0: Cu.reportError(ex); michael@0: } michael@0: // Do not change this to call aCallback directly in try try & catch michael@0: // blocks, because if aCallback throws, we may end up calling aCallback michael@0: // twice. michael@0: aCallback(success); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Gets a string from the migration bundle. Shorthand for michael@0: * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for michael@0: * nsIStringBundle.formatStringFromName if it is. michael@0: * michael@0: * This method also takes care of "bumped" keys (See bug 737381 comment 8 for michael@0: * details). michael@0: * michael@0: * @param aKey michael@0: * The key of the string to retrieve. michael@0: * @param aReplacemts michael@0: * [optioanl] Array of replacements to run on the retrieved string. michael@0: * @return the retrieved string. michael@0: * michael@0: * @see nsIStringBundle michael@0: */ michael@0: getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) { michael@0: const OVERRIDES = { michael@0: "4_firefox": "4_firefox_history_and_bookmarks", michael@0: "64_firefox": "64_firefox_other" michael@0: }; michael@0: aKey = OVERRIDES[aKey] || aKey; michael@0: michael@0: if (aReplacements === undefined) michael@0: return getMigrationBundle().GetStringFromName(aKey); michael@0: return getMigrationBundle().formatStringFromName( michael@0: aKey, aReplacements, aReplacements.length); michael@0: }, michael@0: michael@0: /** michael@0: * Helper for creating a folder for imported bookmarks from a particular michael@0: * migration source. The folder is created at the end of the given folder. michael@0: * michael@0: * @param aSourceNameStr michael@0: * the source name (first letter capitalized). This is used michael@0: * for reading the localized source name from the migration michael@0: * bundle (e.g. if aSourceNameStr is Mosaic, this will try to read michael@0: * sourceNameMosaic from the migration bundle). michael@0: * @param aParentId michael@0: * the item-id of the folder in which the new folder should be michael@0: * created. michael@0: * @return the item-id of the new folder. michael@0: */ michael@0: createImportedBookmarksFolder: michael@0: function MU_createImportedBookmarksFolder(aSourceNameStr, aParentId) { michael@0: let source = this.getLocalizedString("sourceName" + aSourceNameStr); michael@0: let label = this.getLocalizedString("importedBookmarksFolder", [source]); michael@0: return PlacesUtils.bookmarks.createFolder( michael@0: aParentId, label, PlacesUtils.bookmarks.DEFAULT_INDEX); michael@0: }, michael@0: michael@0: get _migrators() gMigrators ? gMigrators : gMigrators = new Dict(), michael@0: michael@0: /* michael@0: * Returns the migrator for the given source, if any data is available michael@0: * for this source, or null otherwise. michael@0: * michael@0: * @param aKey internal name of the migration source. michael@0: * Supported values: ie (windows), michael@0: * safari (mac/windows), michael@0: * chrome (mac/windows/linux), michael@0: * firefox. michael@0: * michael@0: * If null is returned, either no data can be imported michael@0: * for the given migrator, or aMigratorKey is invalid (e.g. ie on mac, michael@0: * or mosaic everywhere). This method should be used rather than direct michael@0: * getService for future compatibility (see bug 718280). michael@0: * michael@0: * @return profile migrator implementing nsIBrowserProfileMigrator, if it can michael@0: * import any data, null otherwise. michael@0: */ michael@0: getMigrator: function MU_getMigrator(aKey) { michael@0: let migrator = null; michael@0: if (this._migrators.has(aKey)) { michael@0: migrator = this._migrators.get(aKey); michael@0: } michael@0: else { michael@0: try { michael@0: migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" + michael@0: aKey].createInstance(Ci.nsIBrowserProfileMigrator); michael@0: } michael@0: catch(ex) { } michael@0: this._migrators.set(aKey, migrator); michael@0: } michael@0: michael@0: return migrator && migrator.sourceExists ? migrator : null; michael@0: }, michael@0: michael@0: // Iterates the available migrators, in the most suitable michael@0: // order for the running platform. michael@0: get migrators() { michael@0: let migratorKeysOrdered = [ michael@0: #ifdef XP_WIN michael@0: "ie", "chrome", "safari" michael@0: #elifdef XP_MACOSX michael@0: "safari", "chrome" michael@0: #elifdef XP_UNIX michael@0: "chrome" michael@0: #endif michael@0: ]; michael@0: michael@0: // If a supported default browser is found check it first michael@0: // so that the wizard defaults to import from that browser. michael@0: let defaultBrowserKey = getMigratorKeyForDefaultBrowser(); michael@0: if (defaultBrowserKey) michael@0: migratorKeysOrdered.sort(function (a, b) b == defaultBrowserKey ? 1 : 0); michael@0: michael@0: for (let migratorKey of migratorKeysOrdered) { michael@0: let migrator = this.getMigrator(migratorKey); michael@0: if (migrator) michael@0: yield migrator; michael@0: } michael@0: }, michael@0: michael@0: // Whether or not we're in the process of startup migration michael@0: get isStartupMigration() gProfileStartup != null, michael@0: michael@0: /** michael@0: * In the case of startup migration, this is set to the nsIProfileStartup michael@0: * instance passed to ProfileMigrator's migrate. michael@0: * michael@0: * @see showMigrationWizard michael@0: */ michael@0: get profileStartup() gProfileStartup, michael@0: michael@0: /** michael@0: * Show the migration wizard. On mac, this may just focus the wizard if it's michael@0: * already running, in which case aOpener and aParams are ignored. michael@0: * michael@0: * @param [optional] aOpener michael@0: * the window that asks to open the wizard. michael@0: * @param [optioanl] aParams michael@0: * arguments for the migration wizard, in the form of an nsIArray. michael@0: * This is passed as-is for the params argument of michael@0: * nsIWindowWatcher.openWindow. michael@0: */ michael@0: showMigrationWizard: michael@0: function MU_showMigrationWizard(aOpener, aParams) { michael@0: let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no"; michael@0: #ifdef XP_MACOSX michael@0: if (!this.isStartupMigration) { michael@0: let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard"); michael@0: if (win) { michael@0: win.focus(); michael@0: return; michael@0: } michael@0: // On mac, the migration wiazrd should only be modal in the case of michael@0: // startup-migration. michael@0: features = "centerscreen,chrome,resizable=no"; michael@0: } michael@0: #endif michael@0: michael@0: Services.ww.openWindow(aOpener, michael@0: "chrome://browser/content/migration/migration.xul", michael@0: "_blank", michael@0: features, michael@0: aParams); michael@0: }, michael@0: michael@0: /** michael@0: * Show the migration wizard for startup-migration. This should only be michael@0: * called by ProfileMigrator (see ProfileMigrator.js), which implements michael@0: * nsIProfileMigrator. michael@0: * michael@0: * @param aProfileStartup michael@0: * the nsIProfileStartup instance provided to ProfileMigrator.migrate. michael@0: * @param [optional] aMigratorKey michael@0: * If set, the migration wizard will import from the corresponding michael@0: * migrator, bypassing the source-selection page. Otherwise, the michael@0: * source-selection page will be displayed, either with the default michael@0: * browser selected, if it could be detected and if there is a michael@0: * migrator for it, or with the first option selected as a fallback michael@0: * (The first option is hardcoded to be the most common browser for michael@0: * the OS we run on. See migration.xul). michael@0: * michael@0: * @throws if aMigratorKey is invalid or if it points to a non-existent michael@0: * source. michael@0: */ michael@0: startupMigration: michael@0: function MU_startupMigrator(aProfileStartup, aMigratorKey) { michael@0: if (!aProfileStartup) { michael@0: throw new Error("an profile-startup instance is required for startup-migration"); michael@0: } michael@0: gProfileStartup = aProfileStartup; michael@0: michael@0: let skipSourcePage = false, migrator = null, migratorKey = ""; michael@0: if (aMigratorKey) { michael@0: migrator = this.getMigrator(aMigratorKey); michael@0: if (!migrator) { michael@0: // aMigratorKey must point to a valid source, so, if it doesn't michael@0: // cleanup and throw. michael@0: this.finishMigration(); michael@0: throw new Error("startMigration was asked to open auto-migrate from " + michael@0: "a non-existent source: " + aMigratorKey); michael@0: } michael@0: migratorKey = aMigratorKey; michael@0: skipSourcePage = true; michael@0: } michael@0: else { michael@0: let defaultBrowserKey = getMigratorKeyForDefaultBrowser(); michael@0: if (defaultBrowserKey) { michael@0: migrator = this.getMigrator(defaultBrowserKey); michael@0: if (migrator) michael@0: migratorKey = defaultBrowserKey; michael@0: } michael@0: } michael@0: michael@0: if (!migrator) { michael@0: // If there's no migrator set so far, ensure that there is at least one michael@0: // migrator available before opening the wizard. michael@0: try { michael@0: this.migrators.next(); michael@0: } michael@0: catch(ex) { michael@0: this.finishMigration(); michael@0: if (!(ex instanceof StopIteration)) michael@0: throw ex; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); michael@0: let keyCSTR = Cc["@mozilla.org/supports-cstring;1"]. michael@0: createInstance(Ci.nsISupportsCString); michael@0: keyCSTR.data = migratorKey; michael@0: let skipImportSourcePageBool = Cc["@mozilla.org/supports-PRBool;1"]. michael@0: createInstance(Ci.nsISupportsPRBool); michael@0: skipImportSourcePageBool.data = skipSourcePage; michael@0: params.appendElement(keyCSTR, false); michael@0: params.appendElement(migrator, false); michael@0: params.appendElement(aProfileStartup, false); michael@0: params.appendElement(skipImportSourcePageBool, false); michael@0: michael@0: this.showMigrationWizard(null, params); michael@0: }, michael@0: michael@0: /** michael@0: * Cleans up references to migrators and nsIProfileInstance instances. michael@0: */ michael@0: finishMigration: function MU_finishMigration() { michael@0: gMigrators = null; michael@0: gProfileStartup = null; michael@0: gMigrationBundle = null; michael@0: } michael@0: });