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.

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

mercurial