|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"]; |
|
8 |
|
9 const Cu = Components.utils; |
|
10 const Ci = Components.interfaces; |
|
11 const Cc = Components.classes; |
|
12 |
|
13 const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks"; |
|
14 const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks"; |
|
15 |
|
16 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
17 Cu.import("resource://gre/modules/Services.jsm"); |
|
18 |
|
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"); |
|
27 |
|
28 let gMigrators = null; |
|
29 let gProfileStartup = null; |
|
30 let gMigrationBundle = null; |
|
31 |
|
32 function getMigrationBundle() { |
|
33 if (!gMigrationBundle) { |
|
34 gMigrationBundle = Services.strings.createBundle( |
|
35 "chrome://browser/locale/migration/migration.properties"); |
|
36 } |
|
37 return gMigrationBundle; |
|
38 } |
|
39 |
|
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 }; |
|
58 |
|
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 } |
|
72 |
|
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]), |
|
91 |
|
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, |
|
104 |
|
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 }, |
|
152 |
|
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, |
|
165 |
|
166 /** |
|
167 * OVERRIDE IF AND ONLY IF your migrator supports importing the homepage. |
|
168 * @see nsIBrowserProfileMigrator |
|
169 */ |
|
170 get sourceHomePageURL() "", |
|
171 |
|
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 }, |
|
182 |
|
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"); |
|
193 |
|
194 if (aItems != Ci.nsIBrowserProfileMigrator.ALL) |
|
195 resources = [r for each (r in resources) if (aItems & r.type)]; |
|
196 |
|
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 }); |
|
208 |
|
209 if (resourcesGroupedByItems.count == 0) |
|
210 throw new Error("No items to import"); |
|
211 |
|
212 let notify = function(aMsg, aItemType) { |
|
213 Services.obs.notifyObservers(null, aMsg, aItemType); |
|
214 } |
|
215 |
|
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); |
|
221 |
|
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 }; |
|
239 |
|
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 } |
|
254 |
|
255 if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) { |
|
256 MigrationUtils.profileStartup.doStartup(); |
|
257 |
|
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, ""); |
|
268 |
|
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 }, |
|
282 |
|
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; |
|
292 |
|
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 }, |
|
313 |
|
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 }; |
|
326 |
|
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 }, |
|
338 |
|
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 }, |
|
390 |
|
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; |
|
413 |
|
414 if (aReplacements === undefined) |
|
415 return getMigrationBundle().GetStringFromName(aKey); |
|
416 return getMigrationBundle().formatStringFromName( |
|
417 aKey, aReplacements, aReplacements.length); |
|
418 }, |
|
419 |
|
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 }, |
|
441 |
|
442 get _migrators() gMigrators ? gMigrators : gMigrators = new Dict(), |
|
443 |
|
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 } |
|
475 |
|
476 return migrator && migrator.sourceExists ? migrator : null; |
|
477 }, |
|
478 |
|
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 ]; |
|
491 |
|
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); |
|
497 |
|
498 for (let migratorKey of migratorKeysOrdered) { |
|
499 let migrator = this.getMigrator(migratorKey); |
|
500 if (migrator) |
|
501 yield migrator; |
|
502 } |
|
503 }, |
|
504 |
|
505 // Whether or not we're in the process of startup migration |
|
506 get isStartupMigration() gProfileStartup != null, |
|
507 |
|
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, |
|
515 |
|
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 |
|
542 |
|
543 Services.ww.openWindow(aOpener, |
|
544 "chrome://browser/content/migration/migration.xul", |
|
545 "_blank", |
|
546 features, |
|
547 aParams); |
|
548 }, |
|
549 |
|
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; |
|
575 |
|
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 } |
|
597 |
|
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 } |
|
611 |
|
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); |
|
623 |
|
624 this.showMigrationWizard(null, params); |
|
625 }, |
|
626 |
|
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 }); |