1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/migration/src/MigrationUtils.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,635 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"]; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +const Ci = Components.interfaces; 1.14 +const Cc = Components.classes; 1.15 + 1.16 +const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks"; 1.17 +const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks"; 1.18 + 1.19 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.20 +Cu.import("resource://gre/modules/Services.jsm"); 1.21 + 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "Dict", 1.23 + "resource://gre/modules/Dict.jsm"); 1.24 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", 1.25 + "resource://gre/modules/PlacesUtils.jsm"); 1.26 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", 1.27 + "resource://gre/modules/NetUtil.jsm"); 1.28 +XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", 1.29 + "resource://gre/modules/BookmarkHTMLUtils.jsm"); 1.30 + 1.31 +let gMigrators = null; 1.32 +let gProfileStartup = null; 1.33 +let gMigrationBundle = null; 1.34 + 1.35 +function getMigrationBundle() { 1.36 + if (!gMigrationBundle) { 1.37 + gMigrationBundle = Services.strings.createBundle( 1.38 + "chrome://browser/locale/migration/migration.properties"); 1.39 + } 1.40 + return gMigrationBundle; 1.41 +} 1.42 + 1.43 +/** 1.44 + * Figure out what is the default browser, and if there is a migrator 1.45 + * for it, return that migrator's internal name. 1.46 + * For the time being, the "internal name" of a migraotr is its contract-id 1.47 + * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie), 1.48 + * but it will soon be exposed properly. 1.49 + */ 1.50 +function getMigratorKeyForDefaultBrowser() { 1.51 + // Don't map Firefox to the Firefox migrator, because we don't 1.52 + // expect it to ever show up as an option in the wizard. 1.53 + // We may want to revise this if/when we use separate profiles 1.54 + // for each Firefox-update channel. 1.55 + const APP_DESC_TO_KEY = { 1.56 + "Internet Explorer": "ie", 1.57 + "Safari": "safari", 1.58 + "Google Chrome": "chrome", // Windows, Linux 1.59 + "Chrome": "chrome", // OS X 1.60 + }; 1.61 + 1.62 + let browserDesc = ""; 1.63 + try { 1.64 + let browserDesc = 1.65 + Cc["@mozilla.org/uriloader/external-protocol-service;1"]. 1.66 + getService(Ci.nsIExternalProtocolService). 1.67 + getApplicationDescription("http"); 1.68 + return APP_DESC_TO_KEY[browserDesc] || ""; 1.69 + } 1.70 + catch(ex) { 1.71 + Cu.reportError("Could not detect default browser: " + ex); 1.72 + } 1.73 + return ""; 1.74 +} 1.75 + 1.76 +/** 1.77 + * Shared prototype for migrators, implementing nsIBrowserProfileMigrator. 1.78 + * 1.79 + * To implement a migrator: 1.80 + * 1. Import this module. 1.81 + * 2. Create the prototype for the migrator, extending MigratorPrototype. 1.82 + * Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype); 1.83 + * 3. Set classDescription, contractID and classID for your migrator, and set 1.84 + * NSGetFactory appropriately. 1.85 + * 4. If the migrator supports multiple profiles, override the sourceProfiles 1.86 + * Here we default for single-profile migrator. 1.87 + * 5. Implement getResources(aProfile) (see below). 1.88 + * 6. If the migrator supports reading the home page of the source browser, 1.89 + * override |sourceHomePageURL| getter. 1.90 + * 7. For startup-only migrators, override |startupOnlyMigrator|. 1.91 + */ 1.92 +this.MigratorPrototype = { 1.93 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserProfileMigrator]), 1.94 + 1.95 + /** 1.96 + * OVERRIDE IF AND ONLY IF the source supports multiple profiles. 1.97 + * 1.98 + * Returns array of profiles (by names) from which data may be imported. 1.99 + * 1.100 + * Only profiles from which data can be imported should be listed. Otherwise 1.101 + * the behavior of the migration wizard isn't well-defined. 1.102 + * 1.103 + * For a single-profile source (e.g. safari, ie), this returns null, 1.104 + * and not an empty array. That is the default implementation. 1.105 + */ 1.106 + get sourceProfiles() null, 1.107 + 1.108 + /** 1.109 + * MUST BE OVERRIDDEN. 1.110 + * 1.111 + * Returns an array of "migration resources" objects for the given profile, 1.112 + * or for the "default" profile, if the migrator does not support multiple 1.113 + * profiles. 1.114 + * 1.115 + * Each migration resource should provide: 1.116 + * - a |type| getter, retunring any of the migration types (see 1.117 + * nsIBrowserProfileMigrator). 1.118 + * 1.119 + * - a |migrate| method, taking a single argument, aCallback(bool success), 1.120 + * for migrating the data for this resource. It may do its job 1.121 + * synchronously or asynchronously. Either way, it must call 1.122 + * aCallback(bool aSuccess) when it's done. In the case of an exception 1.123 + * thrown from |migrate|, it's taken as if aCallback(false) is called. 1.124 + * 1.125 + * Note: In the case of a simple asynchronous implementation, you may find 1.126 + * MigrationUtils.wrapMigrateFunction handy for handling aCallback easily. 1.127 + * 1.128 + * For each migration type listed in nsIBrowserProfileMigrator, multiple 1.129 + * migration resources may be provided. This practice is useful when the 1.130 + * data for a certain migration type is independently stored in few 1.131 + * locations. For example, the mac version of Safari stores its "reading list" 1.132 + * bookmarks in a separate property list. 1.133 + * 1.134 + * Note that the importation of a particular migration type is reported as 1.135 + * successful if _any_ of its resources succeeded to import (that is, called, 1.136 + * |aCallback(true)|). However, completion-status for a particular migration 1.137 + * type is reported to the UI only once all of its migrators have called 1.138 + * aCallback. 1.139 + * 1.140 + * @note The returned array should only include resources from which data 1.141 + * can be imported. So, for example, before adding a resource for the 1.142 + * BOOKMARKS migration type, you should check if you should check that the 1.143 + * bookmarks file exists. 1.144 + * 1.145 + * @param aProfile 1.146 + * The profile from which data may be imported, or an empty string 1.147 + * in the case of a single-profile migrator. 1.148 + * In the case of multiple-profiles migrator, it is guaranteed that 1.149 + * aProfile is a value returned by the sourceProfiles getter (see 1.150 + * above). 1.151 + */ 1.152 + getResources: function MP_getResources(aProfile) { 1.153 + throw new Error("getResources must be overridden"); 1.154 + }, 1.155 + 1.156 + /** 1.157 + * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now, 1.158 + * that is just the Firefox migrator, see bug 737381). Default: false. 1.159 + * 1.160 + * Startup-only migrators are different in two ways: 1.161 + * - they may only be used during startup. 1.162 + * - the user-profile is half baked during migration. The folder exists, 1.163 + * but it's only accessible through MigrationUtils.profileStartup. 1.164 + * The migrator can call MigrationUtils.profileStartup.doStartup 1.165 + * at any point in order to initialize the profile. 1.166 + */ 1.167 + get startupOnlyMigrator() false, 1.168 + 1.169 + /** 1.170 + * OVERRIDE IF AND ONLY IF your migrator supports importing the homepage. 1.171 + * @see nsIBrowserProfileMigrator 1.172 + */ 1.173 + get sourceHomePageURL() "", 1.174 + 1.175 + /** 1.176 + * DO NOT OVERRIDE - After deCOMing migration, the UI will just call 1.177 + * getResources. 1.178 + * 1.179 + * @see nsIBrowserProfileMigrator 1.180 + */ 1.181 + getMigrateData: function MP_getMigrateData(aProfile) { 1.182 + let types = [r.type for each (r in this._getMaybeCachedResources(aProfile))]; 1.183 + return types.reduce(function(a, b) a |= b, 0); 1.184 + }, 1.185 + 1.186 + /** 1.187 + * DO NOT OVERRIDE - After deCOMing migration, the UI will just call 1.188 + * migrate for each resource. 1.189 + * 1.190 + * @see nsIBrowserProfileMigrator 1.191 + */ 1.192 + migrate: function MP_migrate(aItems, aStartup, aProfile) { 1.193 + let resources = this._getMaybeCachedResources(aProfile); 1.194 + if (resources.length == 0) 1.195 + throw new Error("migrate called for a non-existent source"); 1.196 + 1.197 + if (aItems != Ci.nsIBrowserProfileMigrator.ALL) 1.198 + resources = [r for each (r in resources) if (aItems & r.type)]; 1.199 + 1.200 + // Called either directly or through the bookmarks import callback. 1.201 + function doMigrate() { 1.202 + // TODO: use Map (for the items) and Set (for the resources) 1.203 + // once they are iterable. 1.204 + let resourcesGroupedByItems = new Dict(); 1.205 + resources.forEach(function(resource) { 1.206 + if (resourcesGroupedByItems.has(resource.type)) 1.207 + resourcesGroupedByItems.get(resource.type).push(resource); 1.208 + else 1.209 + resourcesGroupedByItems.set(resource.type, [resource]); 1.210 + }); 1.211 + 1.212 + if (resourcesGroupedByItems.count == 0) 1.213 + throw new Error("No items to import"); 1.214 + 1.215 + let notify = function(aMsg, aItemType) { 1.216 + Services.obs.notifyObservers(null, aMsg, aItemType); 1.217 + } 1.218 + 1.219 + notify("Migration:Started"); 1.220 + resourcesGroupedByItems.listkeys().forEach(function(migrationType) { 1.221 + let migrationTypeA = migrationType; 1.222 + let itemResources = resourcesGroupedByItems.get(migrationType); 1.223 + notify("Migration:ItemBeforeMigrate", migrationType); 1.224 + 1.225 + let itemSuccess = false; 1.226 + itemResources.forEach(function(resource) { 1.227 + let resourceDone = function(aSuccess) { 1.228 + let resourceIndex = itemResources.indexOf(resource); 1.229 + if (resourceIndex != -1) { 1.230 + itemResources.splice(resourceIndex, 1); 1.231 + itemSuccess |= aSuccess; 1.232 + if (itemResources.length == 0) { 1.233 + resourcesGroupedByItems.del(migrationType); 1.234 + notify(itemSuccess ? 1.235 + "Migration:ItemAfterMigrate" : "Migration:ItemError", 1.236 + migrationType); 1.237 + if (resourcesGroupedByItems.count == 0) 1.238 + notify("Migration:Ended"); 1.239 + } 1.240 + } 1.241 + }; 1.242 + 1.243 + Services.tm.mainThread.dispatch(function() { 1.244 + // If migrate throws, an error occurred, and the callback 1.245 + // (itemMayBeDone) might haven't been called. 1.246 + try { 1.247 + resource.migrate(resourceDone); 1.248 + } 1.249 + catch(ex) { 1.250 + Cu.reportError(ex); 1.251 + resourceDone(false); 1.252 + } 1.253 + }, Ci.nsIThread.DISPATCH_NORMAL); 1.254 + }); 1.255 + }); 1.256 + } 1.257 + 1.258 + if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) { 1.259 + MigrationUtils.profileStartup.doStartup(); 1.260 + 1.261 + // If we're about to migrate bookmarks, first import the default bookmarks. 1.262 + // Note We do not need to do so for the Firefox migrator 1.263 + // (=startupOnlyMigrator), as it just copies over the places database 1.264 + // from another profile. 1.265 + const BOOKMARKS = MigrationUtils.resourceTypes.BOOKMARKS; 1.266 + let migratingBookmarks = resources.some(function(r) r.type == BOOKMARKS); 1.267 + if (migratingBookmarks) { 1.268 + let browserGlue = Cc["@mozilla.org/browser/browserglue;1"]. 1.269 + getService(Ci.nsIObserver); 1.270 + browserGlue.observe(null, TOPIC_WILL_IMPORT_BOOKMARKS, ""); 1.271 + 1.272 + // Note doMigrate doesn't care about the success of the import. 1.273 + let onImportComplete = function() { 1.274 + browserGlue.observe(null, TOPIC_DID_IMPORT_BOOKMARKS, ""); 1.275 + doMigrate(); 1.276 + }; 1.277 + BookmarkHTMLUtils.importFromURL( 1.278 + "resource:///defaults/profile/bookmarks.html", true).then( 1.279 + onImportComplete, onImportComplete); 1.280 + return; 1.281 + } 1.282 + } 1.283 + doMigrate(); 1.284 + }, 1.285 + 1.286 + /** 1.287 + * DO NOT OVERRIDE - After deCOMing migration, this code 1.288 + * won't be part of the migrator itself. 1.289 + * 1.290 + * @see nsIBrowserProfileMigrator 1.291 + */ 1.292 + get sourceExists() { 1.293 + if (this.startupOnlyMigrator && !MigrationUtils.isStartupMigration) 1.294 + return false; 1.295 + 1.296 + // For a single-profile source, check if any data is available. 1.297 + // For multiple-profiles source, make sure that at least one 1.298 + // profile is available. 1.299 + let exists = false; 1.300 + try { 1.301 + let profiles = this.sourceProfiles; 1.302 + if (!profiles) { 1.303 + let resources = this._getMaybeCachedResources(""); 1.304 + if (resources && resources.length > 0) 1.305 + exists = true; 1.306 + } 1.307 + else { 1.308 + exists = profiles.length > 0; 1.309 + } 1.310 + } 1.311 + catch(ex) { 1.312 + Cu.reportError(ex); 1.313 + } 1.314 + return exists; 1.315 + }, 1.316 + 1.317 + /*** PRIVATE STUFF - DO NOT OVERRIDE ***/ 1.318 + _getMaybeCachedResources: function PMB__getMaybeCachedResources(aProfile) { 1.319 + if (this._resourcesByProfile) { 1.320 + if (aProfile in this._resourcesByProfile) 1.321 + return this._resourcesByProfile[aProfile]; 1.322 + } 1.323 + else { 1.324 + this._resourcesByProfile = { }; 1.325 + } 1.326 + return this._resourcesByProfile[aProfile] = this.getResources(aProfile); 1.327 + } 1.328 +}; 1.329 + 1.330 +this.MigrationUtils = Object.freeze({ 1.331 + resourceTypes: { 1.332 + SETTINGS: Ci.nsIBrowserProfileMigrator.SETTINGS, 1.333 + COOKIES: Ci.nsIBrowserProfileMigrator.COOKIES, 1.334 + HISTORY: Ci.nsIBrowserProfileMigrator.HISTORY, 1.335 + FORMDATA: Ci.nsIBrowserProfileMigrator.FORMDATA, 1.336 + PASSWORDS: Ci.nsIBrowserProfileMigrator.PASSWORDS, 1.337 + BOOKMARKS: Ci.nsIBrowserProfileMigrator.BOOKMARKS, 1.338 + OTHERDATA: Ci.nsIBrowserProfileMigrator.OTHERDATA, 1.339 + SESSION: Ci.nsIBrowserProfileMigrator.SESSION, 1.340 + }, 1.341 + 1.342 + /** 1.343 + * Helper for implementing simple asynchronous cases of migration resources' 1.344 + * |migrate(aCallback)| (see MigratorPrototype). If your |migrate| method 1.345 + * just waits for some file to be read, for example, and then migrates 1.346 + * everything right away, you can wrap the async-function with this helper 1.347 + * and not worry about notifying the callback. 1.348 + * 1.349 + * For example, instead of writing: 1.350 + * setTimeout(function() { 1.351 + * try { 1.352 + * .... 1.353 + * aCallback(true); 1.354 + * } 1.355 + * catch() { 1.356 + * aCallback(false); 1.357 + * } 1.358 + * }, 0); 1.359 + * 1.360 + * You may write: 1.361 + * setTimeout(MigrationUtils.wrapMigrateFunction(function() { 1.362 + * if (importingFromMosaic) 1.363 + * throw Cr.NS_ERROR_UNEXPECTED; 1.364 + * }, aCallback), 0); 1.365 + * 1.366 + * ... and aCallback will be called with aSuccess=false when importing 1.367 + * from Mosaic, or with aSuccess=true otherwise. 1.368 + * 1.369 + * @param aFunction 1.370 + * the function that will be called sometime later. If aFunction 1.371 + * throws when it's called, aCallback(false) is called, otherwise 1.372 + * aCallback(true) is called. 1.373 + * @param aCallback 1.374 + * the callback function passed to |migrate|. 1.375 + * @return the wrapped function. 1.376 + */ 1.377 + wrapMigrateFunction: function MU_wrapMigrateFunction(aFunction, aCallback) { 1.378 + return function() { 1.379 + let success = false; 1.380 + try { 1.381 + aFunction.apply(null, arguments); 1.382 + success = true; 1.383 + } 1.384 + catch(ex) { 1.385 + Cu.reportError(ex); 1.386 + } 1.387 + // Do not change this to call aCallback directly in try try & catch 1.388 + // blocks, because if aCallback throws, we may end up calling aCallback 1.389 + // twice. 1.390 + aCallback(success); 1.391 + } 1.392 + }, 1.393 + 1.394 + /** 1.395 + * Gets a string from the migration bundle. Shorthand for 1.396 + * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for 1.397 + * nsIStringBundle.formatStringFromName if it is. 1.398 + * 1.399 + * This method also takes care of "bumped" keys (See bug 737381 comment 8 for 1.400 + * details). 1.401 + * 1.402 + * @param aKey 1.403 + * The key of the string to retrieve. 1.404 + * @param aReplacemts 1.405 + * [optioanl] Array of replacements to run on the retrieved string. 1.406 + * @return the retrieved string. 1.407 + * 1.408 + * @see nsIStringBundle 1.409 + */ 1.410 + getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) { 1.411 + const OVERRIDES = { 1.412 + "4_firefox": "4_firefox_history_and_bookmarks", 1.413 + "64_firefox": "64_firefox_other" 1.414 + }; 1.415 + aKey = OVERRIDES[aKey] || aKey; 1.416 + 1.417 + if (aReplacements === undefined) 1.418 + return getMigrationBundle().GetStringFromName(aKey); 1.419 + return getMigrationBundle().formatStringFromName( 1.420 + aKey, aReplacements, aReplacements.length); 1.421 + }, 1.422 + 1.423 + /** 1.424 + * Helper for creating a folder for imported bookmarks from a particular 1.425 + * migration source. The folder is created at the end of the given folder. 1.426 + * 1.427 + * @param aSourceNameStr 1.428 + * the source name (first letter capitalized). This is used 1.429 + * for reading the localized source name from the migration 1.430 + * bundle (e.g. if aSourceNameStr is Mosaic, this will try to read 1.431 + * sourceNameMosaic from the migration bundle). 1.432 + * @param aParentId 1.433 + * the item-id of the folder in which the new folder should be 1.434 + * created. 1.435 + * @return the item-id of the new folder. 1.436 + */ 1.437 + createImportedBookmarksFolder: 1.438 + function MU_createImportedBookmarksFolder(aSourceNameStr, aParentId) { 1.439 + let source = this.getLocalizedString("sourceName" + aSourceNameStr); 1.440 + let label = this.getLocalizedString("importedBookmarksFolder", [source]); 1.441 + return PlacesUtils.bookmarks.createFolder( 1.442 + aParentId, label, PlacesUtils.bookmarks.DEFAULT_INDEX); 1.443 + }, 1.444 + 1.445 + get _migrators() gMigrators ? gMigrators : gMigrators = new Dict(), 1.446 + 1.447 + /* 1.448 + * Returns the migrator for the given source, if any data is available 1.449 + * for this source, or null otherwise. 1.450 + * 1.451 + * @param aKey internal name of the migration source. 1.452 + * Supported values: ie (windows), 1.453 + * safari (mac/windows), 1.454 + * chrome (mac/windows/linux), 1.455 + * firefox. 1.456 + * 1.457 + * If null is returned, either no data can be imported 1.458 + * for the given migrator, or aMigratorKey is invalid (e.g. ie on mac, 1.459 + * or mosaic everywhere). This method should be used rather than direct 1.460 + * getService for future compatibility (see bug 718280). 1.461 + * 1.462 + * @return profile migrator implementing nsIBrowserProfileMigrator, if it can 1.463 + * import any data, null otherwise. 1.464 + */ 1.465 + getMigrator: function MU_getMigrator(aKey) { 1.466 + let migrator = null; 1.467 + if (this._migrators.has(aKey)) { 1.468 + migrator = this._migrators.get(aKey); 1.469 + } 1.470 + else { 1.471 + try { 1.472 + migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=" + 1.473 + aKey].createInstance(Ci.nsIBrowserProfileMigrator); 1.474 + } 1.475 + catch(ex) { } 1.476 + this._migrators.set(aKey, migrator); 1.477 + } 1.478 + 1.479 + return migrator && migrator.sourceExists ? migrator : null; 1.480 + }, 1.481 + 1.482 + // Iterates the available migrators, in the most suitable 1.483 + // order for the running platform. 1.484 + get migrators() { 1.485 + let migratorKeysOrdered = [ 1.486 +#ifdef XP_WIN 1.487 + "ie", "chrome", "safari" 1.488 +#elifdef XP_MACOSX 1.489 + "safari", "chrome" 1.490 +#elifdef XP_UNIX 1.491 + "chrome" 1.492 +#endif 1.493 + ]; 1.494 + 1.495 + // If a supported default browser is found check it first 1.496 + // so that the wizard defaults to import from that browser. 1.497 + let defaultBrowserKey = getMigratorKeyForDefaultBrowser(); 1.498 + if (defaultBrowserKey) 1.499 + migratorKeysOrdered.sort(function (a, b) b == defaultBrowserKey ? 1 : 0); 1.500 + 1.501 + for (let migratorKey of migratorKeysOrdered) { 1.502 + let migrator = this.getMigrator(migratorKey); 1.503 + if (migrator) 1.504 + yield migrator; 1.505 + } 1.506 + }, 1.507 + 1.508 + // Whether or not we're in the process of startup migration 1.509 + get isStartupMigration() gProfileStartup != null, 1.510 + 1.511 + /** 1.512 + * In the case of startup migration, this is set to the nsIProfileStartup 1.513 + * instance passed to ProfileMigrator's migrate. 1.514 + * 1.515 + * @see showMigrationWizard 1.516 + */ 1.517 + get profileStartup() gProfileStartup, 1.518 + 1.519 + /** 1.520 + * Show the migration wizard. On mac, this may just focus the wizard if it's 1.521 + * already running, in which case aOpener and aParams are ignored. 1.522 + * 1.523 + * @param [optional] aOpener 1.524 + * the window that asks to open the wizard. 1.525 + * @param [optioanl] aParams 1.526 + * arguments for the migration wizard, in the form of an nsIArray. 1.527 + * This is passed as-is for the params argument of 1.528 + * nsIWindowWatcher.openWindow. 1.529 + */ 1.530 + showMigrationWizard: 1.531 + function MU_showMigrationWizard(aOpener, aParams) { 1.532 + let features = "chrome,dialog,modal,centerscreen,titlebar,resizable=no"; 1.533 +#ifdef XP_MACOSX 1.534 + if (!this.isStartupMigration) { 1.535 + let win = Services.wm.getMostRecentWindow("Browser:MigrationWizard"); 1.536 + if (win) { 1.537 + win.focus(); 1.538 + return; 1.539 + } 1.540 + // On mac, the migration wiazrd should only be modal in the case of 1.541 + // startup-migration. 1.542 + features = "centerscreen,chrome,resizable=no"; 1.543 + } 1.544 +#endif 1.545 + 1.546 + Services.ww.openWindow(aOpener, 1.547 + "chrome://browser/content/migration/migration.xul", 1.548 + "_blank", 1.549 + features, 1.550 + aParams); 1.551 + }, 1.552 + 1.553 + /** 1.554 + * Show the migration wizard for startup-migration. This should only be 1.555 + * called by ProfileMigrator (see ProfileMigrator.js), which implements 1.556 + * nsIProfileMigrator. 1.557 + * 1.558 + * @param aProfileStartup 1.559 + * the nsIProfileStartup instance provided to ProfileMigrator.migrate. 1.560 + * @param [optional] aMigratorKey 1.561 + * If set, the migration wizard will import from the corresponding 1.562 + * migrator, bypassing the source-selection page. Otherwise, the 1.563 + * source-selection page will be displayed, either with the default 1.564 + * browser selected, if it could be detected and if there is a 1.565 + * migrator for it, or with the first option selected as a fallback 1.566 + * (The first option is hardcoded to be the most common browser for 1.567 + * the OS we run on. See migration.xul). 1.568 + * 1.569 + * @throws if aMigratorKey is invalid or if it points to a non-existent 1.570 + * source. 1.571 + */ 1.572 + startupMigration: 1.573 + function MU_startupMigrator(aProfileStartup, aMigratorKey) { 1.574 + if (!aProfileStartup) { 1.575 + throw new Error("an profile-startup instance is required for startup-migration"); 1.576 + } 1.577 + gProfileStartup = aProfileStartup; 1.578 + 1.579 + let skipSourcePage = false, migrator = null, migratorKey = ""; 1.580 + if (aMigratorKey) { 1.581 + migrator = this.getMigrator(aMigratorKey); 1.582 + if (!migrator) { 1.583 + // aMigratorKey must point to a valid source, so, if it doesn't 1.584 + // cleanup and throw. 1.585 + this.finishMigration(); 1.586 + throw new Error("startMigration was asked to open auto-migrate from " + 1.587 + "a non-existent source: " + aMigratorKey); 1.588 + } 1.589 + migratorKey = aMigratorKey; 1.590 + skipSourcePage = true; 1.591 + } 1.592 + else { 1.593 + let defaultBrowserKey = getMigratorKeyForDefaultBrowser(); 1.594 + if (defaultBrowserKey) { 1.595 + migrator = this.getMigrator(defaultBrowserKey); 1.596 + if (migrator) 1.597 + migratorKey = defaultBrowserKey; 1.598 + } 1.599 + } 1.600 + 1.601 + if (!migrator) { 1.602 + // If there's no migrator set so far, ensure that there is at least one 1.603 + // migrator available before opening the wizard. 1.604 + try { 1.605 + this.migrators.next(); 1.606 + } 1.607 + catch(ex) { 1.608 + this.finishMigration(); 1.609 + if (!(ex instanceof StopIteration)) 1.610 + throw ex; 1.611 + return; 1.612 + } 1.613 + } 1.614 + 1.615 + let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); 1.616 + let keyCSTR = Cc["@mozilla.org/supports-cstring;1"]. 1.617 + createInstance(Ci.nsISupportsCString); 1.618 + keyCSTR.data = migratorKey; 1.619 + let skipImportSourcePageBool = Cc["@mozilla.org/supports-PRBool;1"]. 1.620 + createInstance(Ci.nsISupportsPRBool); 1.621 + skipImportSourcePageBool.data = skipSourcePage; 1.622 + params.appendElement(keyCSTR, false); 1.623 + params.appendElement(migrator, false); 1.624 + params.appendElement(aProfileStartup, false); 1.625 + params.appendElement(skipImportSourcePageBool, false); 1.626 + 1.627 + this.showMigrationWizard(null, params); 1.628 + }, 1.629 + 1.630 + /** 1.631 + * Cleans up references to migrators and nsIProfileInstance instances. 1.632 + */ 1.633 + finishMigration: function MU_finishMigration() { 1.634 + gMigrators = null; 1.635 + gProfileStartup = null; 1.636 + gMigrationBundle = null; 1.637 + } 1.638 +});