browser/components/migration/src/MigrationUtils.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"];
     9 const Cu = Components.utils;
    10 const Ci = Components.interfaces;
    11 const Cc = Components.classes;
    13 const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
    14 const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
    16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    17 Cu.import("resource://gre/modules/Services.jsm");
    19 XPCOMUtils.defineLazyModuleGetter(this, "Dict",
    20                                   "resource://gre/modules/Dict.jsm");
    21 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
    22                                   "resource://gre/modules/PlacesUtils.jsm");
    23 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
    24                                   "resource://gre/modules/NetUtil.jsm");
    25 XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
    26                                   "resource://gre/modules/BookmarkHTMLUtils.jsm");
    28 let gMigrators = null;
    29 let gProfileStartup = null;
    30 let gMigrationBundle = null;
    32 function getMigrationBundle() {
    33   if (!gMigrationBundle) {
    34     gMigrationBundle = Services.strings.createBundle(
    35      "chrome://browser/locale/migration/migration.properties"); 
    36   }
    37   return gMigrationBundle;
    38 }
    40 /**
    41  * Figure out what is the default browser, and if there is a migrator
    42  * for it, return that migrator's internal name.
    43  * For the time being, the "internal name" of a migraotr is its contract-id
    44  * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
    45  * but it will soon be exposed properly.
    46  */
    47 function getMigratorKeyForDefaultBrowser() {
    48   // Don't map Firefox to the Firefox migrator, because we don't
    49   // expect it to ever show up as an option in the wizard.
    50   // We may want to revise this if/when we use separate profiles
    51   // for each Firefox-update channel.
    52   const APP_DESC_TO_KEY = {
    53     "Internet Explorer": "ie",
    54     "Safari":            "safari",
    55     "Google Chrome":     "chrome",  // Windows, Linux
    56     "Chrome":            "chrome",  // OS X
    57   };
    59   let browserDesc = "";
    60   try {
    61     let browserDesc =
    62       Cc["@mozilla.org/uriloader/external-protocol-service;1"].
    63       getService(Ci.nsIExternalProtocolService).
    64       getApplicationDescription("http");
    65     return APP_DESC_TO_KEY[browserDesc] || "";
    66   }
    67   catch(ex) {
    68     Cu.reportError("Could not detect default browser: " + ex);
    69   }
    70   return "";
    71 }
    73 /**
    74  * Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
    75  *
    76  * To implement a migrator:
    77  * 1. Import this module.
    78  * 2. Create the prototype for the migrator, extending MigratorPrototype.
    79  *    Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype);
    80  * 3. Set classDescription, contractID and classID for your migrator, and set
    81  *    NSGetFactory appropriately.
    82  * 4. If the migrator supports multiple profiles, override the sourceProfiles
    83  *    Here we default for single-profile migrator.
    84  * 5. Implement getResources(aProfile) (see below).
    85  * 6. If the migrator supports reading the home page of the source browser,
    86  *    override |sourceHomePageURL| getter.
    87  * 7. For startup-only migrators, override |startupOnlyMigrator|.
    88  */
    89 this.MigratorPrototype = {
    90   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]),
    92   /**
    93    * OVERRIDE IF AND ONLY IF the source supports multiple profiles.
    94    *
    95    * Returns array of profiles (by names) from which data may be imported.
    96    *
    97    * Only profiles from which data can be imported should be listed.  Otherwise
    98    * the behavior of the migration wizard isn't well-defined.
    99    *
   100    * For a single-profile source (e.g. safari, ie), this returns null,
   101    * and not an empty array.  That is the default implementation.
   102    */
   103   get sourceProfiles() null,
   105   /**
   106    * MUST BE OVERRIDDEN.
   107    *
   108    * Returns an array of "migration resources" objects for the given profile,
   109    * or for the "default" profile, if the migrator does not support multiple
   110    * profiles.
   111    *
   112    * Each migration resource should provide:
   113    * - a |type| getter, retunring any of the migration types (see
   114    *   nsIBrowserProfileMigrator).
   115    *
   116    * - a |migrate| method, taking a single argument, aCallback(bool success),
   117    *   for migrating the data for this resource.  It may do its job
   118    *   synchronously or asynchronously.  Either way, it must call
   119    *   aCallback(bool aSuccess) when it's done.  In the case of an exception
   120    *   thrown from |migrate|, it's taken as if aCallback(false) is called.
   121    *
   122    *   Note: In the case of a simple asynchronous implementation, you may find
   123    *   MigrationUtils.wrapMigrateFunction handy for handling aCallback easily.
   124    *
   125    * For each migration type listed in nsIBrowserProfileMigrator, multiple
   126    * migration resources may be provided.  This practice is useful when the
   127    * data for a certain migration type is independently stored in few
   128    * locations.  For example, the mac version of Safari stores its "reading list"
   129    * bookmarks in a separate property list.
   130    *
   131    * Note that the importation of a particular migration type is reported as
   132    * successful if _any_ of its resources succeeded to import (that is, called,
   133    * |aCallback(true)|).  However, completion-status for a particular migration
   134    * type is reported to the UI only once all of its migrators have called
   135    * aCallback.
   136    *
   137    * @note  The returned array should only include resources from which data
   138    *        can be imported.  So, for example, before adding a resource for the
   139    *        BOOKMARKS migration type, you should check if you should check that the
   140    *        bookmarks file exists.
   141    *
   142    * @param aProfile
   143    *        The profile from which data may be imported, or an empty string
   144    *        in the case of a single-profile migrator.
   145    *        In the case of multiple-profiles migrator, it is guaranteed that
   146    *        aProfile is a value returned by the sourceProfiles getter (see
   147    *        above).
   148    */
   149   getResources: function MP_getResources(aProfile) {
   150     throw new Error("getResources must be overridden");
   151   },
   153   /**
   154    * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now,
   155    * that is just the Firefox migrator, see bug 737381).  Default: false.
   156    *
   157    * Startup-only migrators are different in two ways:
   158    * - they may only be used during startup.
   159    * - the user-profile is half baked during migration.  The folder exists,
   160    *   but it's only accessible through MigrationUtils.profileStartup.
   161    *   The migrator can call MigrationUtils.profileStartup.doStartup
   162    *   at any point in order to initialize the profile.
   163    */
   164   get startupOnlyMigrator() false,
   166   /**
   167    * OVERRIDE IF AND ONLY IF your migrator supports importing the homepage.
   168    * @see nsIBrowserProfileMigrator
   169    */
   170   get sourceHomePageURL() "",
   172   /**
   173    * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
   174    * getResources.
   175    *
   176    * @see nsIBrowserProfileMigrator
   177    */
   178   getMigrateData: function MP_getMigrateData(aProfile) {
   179     let types = [r.type for each (r in this._getMaybeCachedResources(aProfile))];
   180     return types.reduce(function(a, b) a |= b, 0);
   181   },
   183   /**
   184    * DO NOT OVERRIDE - After deCOMing migration, the UI will just call
   185    * migrate for each resource.
   186    *
   187    * @see nsIBrowserProfileMigrator
   188    */
   189   migrate: function MP_migrate(aItems, aStartup, aProfile) {
   190     let resources = this._getMaybeCachedResources(aProfile);
   191     if (resources.length == 0)
   192       throw new Error("migrate called for a non-existent source");
   194     if (aItems != Ci.nsIBrowserProfileMigrator.ALL)
   195       resources = [r for each (r in resources) if (aItems & r.type)];
   197     // Called either directly or through the bookmarks import callback.
   198     function doMigrate() {
   199       // TODO: use Map (for the items) and Set (for the resources)
   200       // once they are iterable.
   201       let resourcesGroupedByItems = new Dict();
   202       resources.forEach(function(resource) {
   203         if (resourcesGroupedByItems.has(resource.type))
   204           resourcesGroupedByItems.get(resource.type).push(resource);
   205         else
   206           resourcesGroupedByItems.set(resource.type, [resource]);
   207       });
   209       if (resourcesGroupedByItems.count == 0)
   210         throw new Error("No items to import");
   212       let notify = function(aMsg, aItemType) {
   213         Services.obs.notifyObservers(null, aMsg, aItemType);
   214       }
   216       notify("Migration:Started");
   217       resourcesGroupedByItems.listkeys().forEach(function(migrationType) {
   218         let migrationTypeA = migrationType;
   219         let itemResources = resourcesGroupedByItems.get(migrationType);
   220         notify("Migration:ItemBeforeMigrate", migrationType);
   222         let itemSuccess = false;
   223         itemResources.forEach(function(resource) {
   224           let resourceDone = function(aSuccess) {
   225             let resourceIndex = itemResources.indexOf(resource);
   226             if (resourceIndex != -1) {
   227               itemResources.splice(resourceIndex, 1);
   228               itemSuccess |= aSuccess;
   229               if (itemResources.length == 0) {
   230                 resourcesGroupedByItems.del(migrationType);
   231                 notify(itemSuccess ?
   232                        "Migration:ItemAfterMigrate" : "Migration:ItemError",
   233                        migrationType);
   234                 if (resourcesGroupedByItems.count == 0)
   235                   notify("Migration:Ended");
   236               }
   237             }
   238           };
   240           Services.tm.mainThread.dispatch(function() {
   241             // If migrate throws, an error occurred, and the callback
   242             // (itemMayBeDone) might haven't been called.
   243             try {
   244               resource.migrate(resourceDone);
   245             }
   246             catch(ex) {
   247               Cu.reportError(ex);
   248               resourceDone(false);
   249             }
   250           }, Ci.nsIThread.DISPATCH_NORMAL);
   251         });
   252       });
   253     }
   255     if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) {
   256       MigrationUtils.profileStartup.doStartup();
   258       // If we're about to migrate bookmarks, first import the default bookmarks.
   259       // Note We do not need to do so for the Firefox migrator
   260       // (=startupOnlyMigrator), as it just copies over the places database
   261       // from another profile.
   262       const BOOKMARKS = MigrationUtils.resourceTypes.BOOKMARKS;
   263       let migratingBookmarks = resources.some(function(r) r.type == BOOKMARKS);
   264       if (migratingBookmarks) {
   265         let browserGlue = Cc["@mozilla.org/browser/browserglue;1"].
   266                           getService(Ci.nsIObserver);
   267         browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, "");
   269         // Note doMigrate doesn't care about the success of the import.
   270         let onImportComplete = function() {
   271           browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, "");
   272           doMigrate();
   273         };
   274         BookmarkHTMLUtils.importFromURL(
   275           "resource:///defaults/profile/bookmarks.html", true).then(
   276           onImportComplete, onImportComplete);
   277         return;
   278       }
   279     }
   280     doMigrate();
   281   },
   283   /**
   284    * DO NOT OVERRIDE - After deCOMing migration, this code
   285    * won't be part of the migrator itself.
   286    *
   287    * @see nsIBrowserProfileMigrator
   288    */
   289   get sourceExists() {
   290     if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration)
   291       return false;
   293     // For a single-profile source, check if any data is available.
   294     // For multiple-profiles source, make sure that at least one
   295     // profile is available.
   296     let exists = false;
   297     try {
   298       let profiles = this.sourceProfiles;
   299       if (!profiles) {
   300         let resources = this._getMaybeCachedResources("");
   301         if (resources && resources.length > 0)
   302           exists = true;
   303       }
   304       else {
   305         exists = profiles.length > 0;
   306       }
   307     }
   308     catch(ex) {
   309       Cu.reportError(ex);
   310     }
   311     return exists;
   312   },
   314   /*** PRIVATE STUFF - DO NOT OVERRIDE ***/
   315   _getMaybeCachedResources: function PMB__getMaybeCachedResources(aProfile) {
   316     if (this._resourcesByProfile) {
   317       if (aProfile in this._resourcesByProfile)
   318         return this._resourcesByProfile[aProfile];
   319     }
   320     else {
   321       this._resourcesByProfile = { };
   322     }
   323     return this._resourcesByProfile[aProfile] = this.getResources(aProfile);
   324   }
   325 };
   327 this.MigrationUtils = Object.freeze({
   328   resourceTypes: {
   329     SETTINGS:   Ci.nsIBrowserProfileMigrator.SETTINGS,
   330     COOKIES:    Ci.nsIBrowserProfileMigrator.COOKIES,
   331     HISTORY:    Ci.nsIBrowserProfileMigrator.HISTORY,
   332     FORMDATA:   Ci.nsIBrowserProfileMigrator.FORMDATA,
   333     PASSWORDS:  Ci.nsIBrowserProfileMigrator.PASSWORDS,
   334     BOOKMARKS:  Ci.nsIBrowserProfileMigrator.BOOKMARKS,
   335     OTHERDATA:  Ci.nsIBrowserProfileMigrator.OTHERDATA,
   336     SESSION:    Ci.nsIBrowserProfileMigrator.SESSION,
   337   },
   339   /**
   340    * Helper for implementing simple asynchronous cases of migration resources'
   341    * |migrate(aCallback)| (see MigratorPrototype).  If your |migrate| method
   342    * just waits for some file to be read, for example, and then migrates
   343    * everything right away, you can wrap the async-function with this helper
   344    * and not worry about notifying the callback.
   345    *
   346    * For example, instead of writing:
   347    * setTimeout(function() {
   348    *   try {
   349    *     ....
   350    *     aCallback(true);
   351    *   }
   352    *   catch() {
   353    *     aCallback(false);
   354    *   }
   355    * }, 0);
   356    *
   357    * You may write:
   358    * setTimeout(MigrationUtils.wrapMigrateFunction(function() {
   359    *   if (importingFromMosaic)
   360    *     throw Cr.NS_ERROR_UNEXPECTED;
   361    * }, aCallback), 0);
   362    *
   363    * ... and aCallback will be called with aSuccess=false when importing
   364    * from Mosaic, or with aSuccess=true otherwise.
   365    *
   366    * @param aFunction
   367    *        the function that will be called sometime later.  If aFunction
   368    *        throws when it's called, aCallback(false) is called, otherwise
   369    *        aCallback(true) is called.
   370    * @param aCallback
   371    *        the callback function passed to |migrate|.
   372    * @return the wrapped function.
   373    */
   374   wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) {
   375     return function() {
   376       let success = false;
   377       try {
   378         aFunction.apply(null, arguments);
   379         success = true;
   380       }
   381       catch(ex) {
   382         Cu.reportError(ex);
   383       }
   384       // Do not change this to call aCallback directly in try try & catch
   385       // blocks, because if aCallback throws, we may end up calling aCallback
   386       // twice.
   387       aCallback(success);
   388     }
   389   },
   391   /**
   392    * Gets a string from the migration bundle.  Shorthand for
   393    * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for
   394    * nsIStringBundle.formatStringFromName if it is.
   395    *
   396    * This method also takes care of "bumped" keys (See bug 737381 comment 8 for
   397    * details).
   398    *
   399    * @param aKey
   400    *        The key of the string to retrieve.
   401    * @param aReplacemts
   402    *        [optioanl] Array of replacements to run on the retrieved string.
   403    * @return the retrieved string.
   404    *
   405    * @see nsIStringBundle
   406    */
   407   getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) {
   408     const OVERRIDES = {
   409       "4_firefox": "4_firefox_history_and_bookmarks",
   410       "64_firefox": "64_firefox_other"
   411     };
   412     aKey = OVERRIDES[aKey] || aKey;
   414     if (aReplacements === undefined)
   415       return getMigrationBundle().GetStringFromName(aKey);
   416     return getMigrationBundle().formatStringFromName(
   417       aKey, aReplacements, aReplacements.length);
   418   },
   420   /**
   421    * Helper for creating a folder for imported bookmarks from a particular
   422    * migration source.  The folder is created at the end of the given folder.
   423    *
   424    * @param aSourceNameStr
   425    *        the source name (first letter capitalized).  This is used
   426    *        for reading the localized source name from the migration
   427    *        bundle (e.g. if aSourceNameStr is Mosaic, this will try to read
   428    *        sourceNameMosaic from the migration bundle).
   429    * @param aParentId
   430    *        the item-id of the folder in which the new folder should be
   431    *        created.
   432    * @return the item-id of the new folder.
   433    */
   434   createImportedBookmarksFolder:
   435   function MU_createImportedBookmarksFolder(aSourceNameStr, aParentId) {
   436     let source = this.getLocalizedString("sourceName" + aSourceNameStr);
   437     let label = this.getLocalizedString("importedBookmarksFolder", [source]);
   438     return PlacesUtils.bookmarks.createFolder(
   439       aParentId, label, PlacesUtils.bookmarks.DEFAULT_INDEX);
   440   },
   442   get _migrators() gMigrators ? gMigrators : gMigrators = new Dict(),
   444   /*
   445    * Returns the migrator for the given source, if any data is available
   446    * for this source, or null otherwise.
   447    *
   448    * @param aKey internal name of the migration source.
   449    *             Supported values: ie (windows),
   450    *                               safari (mac/windows),
   451    *                               chrome (mac/windows/linux),
   452    *                               firefox.
   453    *
   454    * If null is returned,  either no data can be imported
   455    * for the given migrator, or aMigratorKey is invalid  (e.g. ie on mac,
   456    * or mosaic everywhere).  This method should be used rather than direct
   457    * getService for future compatibility (see bug 718280).
   458    *
   459    * @return profile migrator implementing nsIBrowserProfileMigrator, if it can
   460    *         import any data, null otherwise.
   461    */
   462   getMigrator: function MU_getMigrator(aKey) {
   463     let migrator = null;
   464     if (this._migrators.has(aKey)) {
   465       migrator = this._migrators.get(aKey);
   466     }
   467     else {
   468       try {
   469         migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" +
   470                       aKey].createInstance(Ci.nsIBrowserProfileMigrator);
   471       }
   472       catch(ex) { }
   473       this._migrators.set(aKey, migrator);
   474     }
   476     return migrator && migrator.sourceExists ? migrator : null;
   477   },
   479   // Iterates the available migrators, in the most suitable
   480   // order for the running platform.
   481   get migrators() {
   482     let migratorKeysOrdered = [
   483 #ifdef XP_WIN
   484       "ie", "chrome", "safari"
   485 #elifdef XP_MACOSX
   486       "safari", "chrome"
   487 #elifdef XP_UNIX
   488       "chrome"
   489 #endif
   490     ];
   492     // If a supported default browser is found check it first
   493     // so that the wizard defaults to import from that browser.
   494     let defaultBrowserKey = getMigratorKeyForDefaultBrowser();
   495     if (defaultBrowserKey)
   496       migratorKeysOrdered.sort(function (a, b) b == defaultBrowserKey ? 1 : 0);
   498     for (let migratorKey of migratorKeysOrdered) {
   499       let migrator = this.getMigrator(migratorKey);
   500       if (migrator)
   501         yield migrator;
   502     }
   503   },
   505   // Whether or not we're in the process of startup migration
   506   get isStartupMigration() gProfileStartup != null,
   508   /**
   509    * In the case of startup migration, this is set to the nsIProfileStartup
   510    * instance passed to ProfileMigrator's migrate.
   511    *
   512    * @see showMigrationWizard
   513    */
   514   get profileStartup() gProfileStartup,
   516   /**
   517    * Show the migration wizard.  On mac, this may just focus the wizard if it's
   518    * already running, in which case aOpener and aParams are ignored.
   519    *
   520    * @param [optional] aOpener
   521    *        the window that asks to open the wizard.
   522    * @param [optioanl] aParams
   523    *        arguments for the migration wizard, in the form of an nsIArray.
   524    *        This is passed as-is for the params argument of
   525    *        nsIWindowWatcher.openWindow.
   526    */
   527   showMigrationWizard:
   528   function MU_showMigrationWizard(aOpener, aParams) {
   529     let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no";
   530 #ifdef XP_MACOSX
   531     if (!this.isStartupMigration) {
   532       let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard");
   533       if (win) {
   534         win.focus();
   535         return;
   536       }
   537       // On mac, the migration wiazrd should only be modal in the case of
   538       // startup-migration.
   539       features = "centerscreen,chrome,resizable=no";
   540     }
   541 #endif
   543     Services.ww.openWindow(aOpener,
   544                            "chrome://browser/content/migration/migration.xul",
   545                            "_blank",
   546                            features,
   547                            aParams);
   548   },
   550   /**
   551    * Show the migration wizard for startup-migration.  This should only be
   552    * called by ProfileMigrator (see ProfileMigrator.js), which implements
   553    * nsIProfileMigrator.
   554    *
   555    * @param aProfileStartup
   556    *        the nsIProfileStartup instance provided to ProfileMigrator.migrate.
   557    * @param [optional] aMigratorKey
   558    *        If set, the migration wizard will import from the corresponding
   559    *        migrator, bypassing the source-selection page.  Otherwise, the
   560    *        source-selection page will be displayed, either with the default
   561    *        browser selected, if it could be detected and if there is a
   562    *        migrator for it, or with the first option selected as a fallback
   563    *        (The first option is hardcoded to be the most common browser for
   564    *         the OS we run on.  See migration.xul).
   565    *          
   566    * @throws if aMigratorKey is invalid or if it points to a non-existent
   567    *         source.
   568    */
   569   startupMigration:
   570   function MU_startupMigrator(aProfileStartup, aMigratorKey) {
   571     if (!aProfileStartup) {
   572       throw new Error("an profile-startup instance is required for startup-migration");
   573     }
   574     gProfileStartup = aProfileStartup;
   576     let skipSourcePage = false, migrator = null, migratorKey = "";
   577     if (aMigratorKey) {
   578       migrator = this.getMigrator(aMigratorKey);
   579       if (!migrator) {
   580         // aMigratorKey must point to a valid source, so, if it doesn't
   581         // cleanup and throw.
   582         this.finishMigration();
   583         throw new Error("startMigration was asked to open auto-migrate from " +
   584                         "a non-existent source: " + aMigratorKey);
   585       }
   586       migratorKey = aMigratorKey;
   587       skipSourcePage = true;
   588     }
   589     else {
   590       let defaultBrowserKey = getMigratorKeyForDefaultBrowser();
   591       if (defaultBrowserKey) {
   592         migrator = this.getMigrator(defaultBrowserKey);
   593         if (migrator)
   594           migratorKey = defaultBrowserKey;
   595       }
   596     }
   598     if (!migrator) {
   599       // If there's no migrator set so far, ensure that there is at least one
   600       // migrator available before opening the wizard.
   601       try {
   602         this.migrators.next();
   603       }
   604       catch(ex) {
   605         this.finishMigration();
   606         if (!(ex instanceof StopIteration))
   607           throw ex;
   608         return;
   609       }
   610     }
   612     let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
   613     let keyCSTR = Cc["@mozilla.org/supports-cstring;1"].
   614                   createInstance(Ci.nsISupportsCString);
   615     keyCSTR.data = migratorKey;
   616     let skipImportSourcePageBool = Cc["@mozilla.org/supports-PRBool;1"].
   617                                    createInstance(Ci.nsISupportsPRBool);
   618     skipImportSourcePageBool.data = skipSourcePage;
   619     params.appendElement(keyCSTR, false);
   620     params.appendElement(migrator, false);
   621     params.appendElement(aProfileStartup, false);
   622     params.appendElement(skipImportSourcePageBool, false);
   624     this.showMigrationWizard(null, params);
   625   },
   627   /**
   628    * Cleans up references to migrators and nsIProfileInstance instances.
   629    */
   630   finishMigration: function MU_finishMigration() {
   631     gMigrators = null;
   632     gProfileStartup = null;
   633     gMigrationBundle = null;
   634   }
   635 });

mercurial