browser/components/migration/src/SafariProfileMigrator.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:17252aba93db
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 let Cc = Components.classes;
8 let Ci = Components.interfaces;
9 let Cu = Components.utils;
10
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
12 Cu.import("resource://gre/modules/FileUtils.jsm");
13 Cu.import("resource://gre/modules/Services.jsm");
14 Cu.import("resource:///modules/MigrationUtils.jsm");
15
16 XPCOMUtils.defineLazyModuleGetter(this, "PropertyListUtils",
17 "resource://gre/modules/PropertyListUtils.jsm");
18 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
19 "resource://gre/modules/PlacesUtils.jsm");
20 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
21 "resource://gre/modules/NetUtil.jsm");
22 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
23 "resource://gre/modules/FormHistory.jsm");
24
25 function Bookmarks(aBookmarksFile) {
26 this._file = aBookmarksFile;
27 }
28 Bookmarks.prototype = {
29 type: MigrationUtils.resourceTypes.BOOKMARKS,
30
31 migrate: function B_migrate(aCallback) {
32 PropertyListUtils.read(this._file,
33 MigrationUtils.wrapMigrateFunction(function migrateBookmarks(aDict) {
34 if (!aDict)
35 throw new Error("Could not read Bookmarks.plist");
36
37 let children = aDict.get("Children");;
38 if (!children)
39 throw new Error("Invalid Bookmarks.plist format");
40
41 PlacesUtils.bookmarks.runInBatchMode({
42 runBatched: function() {
43 let collection = aDict.get("Title") == "com.apple.ReadingList" ?
44 this.READING_LIST_COLLECTION : this.ROOT_COLLECTION;
45 this._migrateCollection(children, collection);
46 }.bind(this)
47 }, null);
48 }.bind(this), aCallback));
49 },
50
51 // Bookmarks collections in Safari. Constants for migrateCollection.
52 ROOT_COLLECTION: 0,
53 MENU_COLLECTION: 1,
54 TOOLBAR_COLLECTION: 2,
55 READING_LIST_COLLECTION: 3,
56
57 /**
58 * Recursively migrate a Safari collection of bookmarks.
59 *
60 * @param aEntries
61 * the collection's children
62 * @param aCollection
63 * one of the values above.
64 */
65 _migrateCollection: function B__migrateCollection(aEntries, aCollection) {
66 // A collection of bookmarks in Safari resembles places roots. In the
67 // property list files (Bookmarks.plist, ReadingList.plist) they are
68 // stored as regular bookmarks folders, and thus can only be distinguished
69 // from by their names and places in the hierarchy.
70
71 let entriesFiltered = [];
72 if (aCollection == this.ROOT_COLLECTION) {
73 for (let entry of aEntries) {
74 let type = entry.get("WebBookmarkType");
75 if (type == "WebBookmarkTypeList" && entry.has("Children")) {
76 let title = entry.get("Title");
77 let children = entry.get("Children");
78 if (title == "BookmarksBar")
79 this._migrateCollection(children, this.TOOLBAR_COLLECTION);
80 else if (title == "BookmarksMenu")
81 this._migrateCollection(children, this.MENU_COLLECTION);
82 else if (title == "com.apple.ReadingList")
83 this._migrateCollection(children, this.READING_LIST_COLLECTION);
84 else if (entry.get("ShouldOmitFromUI") !== true)
85 entriesFiltered.push(entry);
86 }
87 else if (type == "WebBookmarkTypeLeaf") {
88 entriesFiltered.push(entry);
89 }
90 }
91 }
92 else {
93 entriesFiltered = aEntries;
94 }
95
96 if (entriesFiltered.length == 0)
97 return;
98
99 let folder = -1;
100 switch (aCollection) {
101 case this.ROOT_COLLECTION: {
102 // In Safari, it is possible (though quite cumbersome) to move
103 // bookmarks to the bookmarks root, which is the parent folder of
104 // all bookmarks "collections". That is somewhat in parallel with
105 // both the places root and the unfiled-bookmarks root.
106 // Because the former is only an implementation detail in our UI,
107 // the unfiled root seems to be the best choice.
108 folder = PlacesUtils.unfiledBookmarksFolderId;
109 break;
110 }
111 case this.MENU_COLLECTION: {
112 folder = PlacesUtils.bookmarksMenuFolderId;
113 if (!MigrationUtils.isStartupMigration) {
114 folder = MigrationUtils.createImportedBookmarksFolder("Safari",
115 folder);
116 }
117 break;
118 }
119 case this.TOOLBAR_COLLECTION: {
120 folder = PlacesUtils.toolbarFolderId;
121 if (!MigrationUtils.isStartupMigration) {
122 folder = MigrationUtils.createImportedBookmarksFolder("Safari",
123 folder);
124 }
125 break;
126 }
127 case this.READING_LIST_COLLECTION: {
128 // Reading list items are imported as regular bookmarks.
129 // They are imported under their own folder, created either under the
130 // bookmarks menu (in the case of startup migration).
131 folder = PlacesUtils.bookmarks.createFolder(
132 PlacesUtils.bookmarksMenuFolderId,
133 MigrationUtils.getLocalizedString("importedSafariReadingList"),
134 PlacesUtils.bookmarks.DEFAULT_INDEX);
135 break;
136 }
137 default:
138 throw new Error("Unexpected value for aCollection!");
139 }
140
141 this._migrateEntries(entriesFiltered, folder);
142 },
143
144 // migrate the given array of safari bookmarks to the given places
145 // folder.
146 _migrateEntries: function B__migrateEntries(aEntries, aFolderId) {
147 for (let entry of aEntries) {
148 let type = entry.get("WebBookmarkType");
149 if (type == "WebBookmarkTypeList" && entry.has("Children")) {
150 let title = entry.get("Title");
151 let folderId = PlacesUtils.bookmarks.createFolder(
152 aFolderId, title, PlacesUtils.bookmarks.DEFAULT_INDEX);
153
154 // Empty folders may not have a children array.
155 if (entry.has("Children"))
156 this._migrateEntries(entry.get("Children"), folderId, false);
157 }
158 else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
159 let title, uri;
160 if (entry.has("URIDictionary"))
161 title = entry.get("URIDictionary").get("title");
162
163 try {
164 uri = NetUtil.newURI(entry.get("URLString"));
165 }
166 catch(ex) {
167 Cu.reportError("Invalid uri set for Safari bookmark: " + entry.get("URLString"));
168 }
169 if (uri) {
170 PlacesUtils.bookmarks.insertBookmark(aFolderId, uri,
171 PlacesUtils.bookmarks.DEFAULT_INDEX, title);
172 }
173 }
174 }
175 }
176 };
177
178 function History(aHistoryFile) {
179 this._file = aHistoryFile;
180 }
181 History.prototype = {
182 type: MigrationUtils.resourceTypes.HISTORY,
183
184 // Helper method for converting the visit date property to a PRTime value.
185 // The visit date is stored as a string, so it's not read as a Date
186 // object by PropertyListUtils.
187 _parseCocoaDate: function H___parseCocoaDate(aCocoaDateStr) {
188 let asDouble = parseFloat(aCocoaDateStr);
189 if (!isNaN(asDouble)) {
190 // reference date of NSDate.
191 let date = new Date("1 January 2001, GMT");
192 date.setMilliseconds(asDouble * 1000);
193 return date * 1000;
194 }
195 return 0;
196 },
197
198 migrate: function H_migrate(aCallback) {
199 PropertyListUtils.read(this._file, function migrateHistory(aDict) {
200 try {
201 if (!aDict)
202 throw new Error("Could not read history property list");
203 if (!aDict.has("WebHistoryDates"))
204 throw new Error("Unexpected history-property list format");
205
206 // Safari's History file contains only top-level urls. It does not
207 // distinguish between typed urls and linked urls.
208 let transType = PlacesUtils.history.TRANSITION_LINK;
209
210 let places = [];
211 let entries = aDict.get("WebHistoryDates");
212 for (let entry of entries) {
213 if (entry.has("lastVisitedDate")) {
214 let visitDate = this._parseCocoaDate(entry.get("lastVisitedDate"));
215 try {
216 places.push({ uri: NetUtil.newURI(entry.get("")),
217 title: entry.get("title"),
218 visits: [{ transitionType: transType,
219 visitDate: visitDate }] });
220 }
221 catch(ex) {
222 // Safari's History file may contain malformed URIs which
223 // will be ignored.
224 Cu.reportError(ex)
225 }
226 }
227 }
228 if (places.length > 0) {
229 PlacesUtils.asyncHistory.updatePlaces(places, {
230 _success: false,
231 handleResult: function() {
232 // Importing any entry is considered a successful import.
233 this._success = true;
234 },
235 handleError: function() {},
236 handleCompletion: function() {
237 aCallback(this._success);
238 }
239 });
240 }
241 else {
242 aCallback(false);
243 }
244 }
245 catch(ex) {
246 Cu.reportError(ex);
247 aCallback(false);
248 }
249 }.bind(this));
250 }
251 };
252
253 /**
254 * Safari's preferences property list is independently used for three purposes:
255 * (a) importation of preferences
256 * (b) importation of search strings
257 * (c) retrieving the home page.
258 *
259 * So, rather than reading it three times, it's cached and managed here.
260 */
261 function MainPreferencesPropertyList(aPreferencesFile) {
262 this._file = aPreferencesFile;
263 this._callbacks = [];
264 }
265 MainPreferencesPropertyList.prototype = {
266 /**
267 * @see PropertyListUtils.read
268 */
269 read: function MPPL_read(aCallback) {
270 if ("_dict" in this) {
271 aCallback(this._dict);
272 return;
273 }
274
275 let alreadyReading = this._callbacks.length > 0;
276 this._callbacks.push(aCallback);
277 if (!alreadyReading) {
278 PropertyListUtils.read(this._file, function readPrefs(aDict) {
279 this._dict = aDict;
280 for (let callback of this._callbacks) {
281 try {
282 callback(aDict);
283 }
284 catch(ex) {
285 Cu.reportError(ex);
286 }
287 }
288 this._callbacks.splice(0);
289 }.bind(this));
290 }
291 },
292
293 // Workaround for nsIBrowserProfileMigrator.sourceHomePageURL until
294 // it's replaced with an async method.
295 _readSync: function MPPL__readSync() {
296 if ("_dict" in this)
297 return this._dict;
298
299 let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
300 createInstance(Ci.nsIFileInputStream);
301 inputStream.init(this._file, -1, -1, 0);
302 let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
303 createInstance(Ci.nsIBinaryInputStream);
304 binaryStream.setInputStream(inputStream);
305 let bytes = binaryStream.readByteArray(inputStream.available());
306 this._dict = PropertyListUtils._readFromArrayBufferSync(
307 new Uint8Array(bytes).buffer);
308 return this._dict;
309 }
310 };
311
312 function Preferences(aMainPreferencesPropertyListInstance) {
313 this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
314 }
315 Preferences.prototype = {
316 type: MigrationUtils.resourceTypes.SETTINGS,
317
318 migrate: function MPR_migrate(aCallback) {
319 this._mainPreferencesPropertyList.read(
320 MigrationUtils.wrapMigrateFunction(function migratePrefs(aDict) {
321 if (!aDict)
322 throw new Error("Could not read preferences file");
323
324 this._dict = aDict;
325
326 let invert = function(webkitVal) !webkitVal;
327 this._set("AutoFillPasswords", "signon.rememberSignons");
328 this._set("OpenNewTabsInFront", "browser.tabs.loadInBackground", invert);
329 this._set("WebKitJavaScriptCanOpenWindowsAutomatically",
330 "dom.disable_open_during_load", invert);
331
332 // layout.spellcheckDefault is a boolean stored as a number.
333 this._set("WebContinuousSpellCheckingEnabled",
334 "layout.spellcheckDefault", Number);
335
336 // Auto-load images
337 // Firefox has an elaborate set of Image preferences. The correlation is:
338 // Mode: Safari Firefox
339 // Blocked FALSE 2
340 // Allowed TRUE 1
341 // Allowed, originating site only -- 3
342 this._set("WebKitDisplayImagesKey", "permissions.default.image",
343 function(webkitVal) webkitVal ? 1 : 2);
344
345 #ifdef XP_WIN
346 // Cookie-accept policy.
347 // For the OS X version, see WebFoundationCookieBehavior.
348 // Setting Safari Firefox
349 // Always Accept 0 0
350 // Accept from Originating 2 1
351 // Never Accept 1 2
352 this._set("WebKitCookieStorageAcceptPolicy",
353 "network.cookie.cookieBehavior",
354 function(webkitVal) webkitVal == 0 ? 0 : webkitVal == 1 ? 2 : 1);
355 #endif
356
357 this._migrateFontSettings();
358 this._migrateDownloadsFolder();
359 }.bind(this), aCallback));
360 },
361
362 /**
363 * Attempts to migrates a preference from Safari. Returns whether the preference
364 * has been migrated.
365 * @param aSafariKey
366 * The dictionary key for the preference of Safari.
367 * @param aMozPref
368 * The gecko/firefox preference to which aSafariKey should be migrated
369 * @param [optional] aConvertFunction(aSafariValue)
370 * a function that converts the safari-preference value to the
371 * appropriate value for aMozPref. If it's not passed, then the
372 * Safari value is set as is.
373 * If aConvertFunction returns undefined, then aMozPref is not set
374 * at all.
375 * @return whether or not aMozPref was set.
376 */
377 _set: function MPR_set(aSafariKey, aMozPref, aConvertFunction) {
378 if (this._dict.has(aSafariKey)) {
379 let safariVal = this._dict.get(aSafariKey);
380 let mozVal = aConvertFunction !== undefined ?
381 aConvertFunction(safariVal) : safariVal;
382 switch (typeof(mozVal)) {
383 case "string":
384 Services.prefs.setCharPref(aMozPref, mozVal);
385 break;
386 case "number":
387 Services.prefs.setIntPref(aMozPref, mozVal);
388 break;
389 case "boolean":
390 Services.prefs.setBoolPref(aMozPref, mozVal);
391 break;
392 case "undefined":
393 return false;
394 default:
395 throw new Error("Unexpected value type: " + typeof(mozVal));
396 }
397 }
398 return true;
399 },
400
401 // Fonts settings are quite problematic for migration, for a couple of
402 // reasons:
403 // (a) Every font preference in Gecko is set for a particular language.
404 // In Safari, each font preference applies to all languages.
405 // (b) The current underlying implementation of nsIFontEnumerator cannot
406 // really tell you anything about a font: no matter what language or type
407 // you try to enumerate with EnumerateFonts, you get an array of all
408 // fonts in the systems (This also breaks our fonts dialog).
409 // (c) In Gecko, each langauge has a distinct serif and sans-serif font
410 // preference. Safari has only one default font setting. It seems that
411 // it checks if it's a serif or sans serif font, and when a site
412 // explicitly asks to use serif/sans-serif font, it uses the default font
413 // only if it applies to this type.
414 // (d) The solution of guessing the lang-group out of the default charset (as
415 // done in the old Safari migrator) can only work when:
416 // (1) The default charset preference is set.
417 // (2) It's not a unicode charset.
418 // For now, we use the language implied by the system locale as the
419 // lang-group. The only exception is minimal font size, which is an
420 // accessibility preference in Safari (under the Advanced tab). If it is set,
421 // we set it for all languages.
422 // As for the font type of the default font (serif/sans-serif), the default
423 // type for the given language is used (set in font.default.LANGGROUP).
424 _migrateFontSettings: function MPR__migrateFontSettings() {
425 // If "Never use font sizes smaller than [ ] is set", migrate it for all
426 // languages.
427 if (this._dict.has("WebKitMinimumFontSize")) {
428 let minimumSize = this._dict.get("WebKitMinimumFontSize");
429 if (typeof(minimumSize) == "number") {
430 let prefs = Services.prefs.getChildList("font.minimum-size");
431 for (let pref of prefs) {
432 Services.prefs.setIntPref(pref, minimumSize);
433 }
434 }
435 else {
436 Cu.reportError("WebKitMinimumFontSize was set to an invalid value: " +
437 minimumSize);
438 }
439 }
440
441 // In theory, the lang group could be "x-unicode". This will result
442 // in setting the fonts for "Other Languages".
443 let lang = this._getLocaleLangGroup();
444
445 let anySet = false;
446 let fontType = Services.prefs.getCharPref("font.default." + lang);
447 anySet |= this._set("WebKitFixedFont", "font.name.monospace." + lang);
448 anySet |= this._set("WebKitDefaultFixedFontSize", "font.size.fixed." + lang);
449 anySet |= this._set("WebKitStandardFont",
450 "font.name." + fontType + "." + lang);
451 anySet |= this._set("WebKitDefaultFontSize", "font.size.variable." + lang);
452
453 // If we set font settings for a particular language, we'll also set the
454 // fonts dialog to open with the fonts settings for that langauge.
455 if (anySet)
456 Services.prefs.setCharPref("font.language.group", lang);
457 },
458
459 // Get the language group for the system locale.
460 _getLocaleLangGroup: function MPR__getLocaleLangGroup() {
461 let locale = Services.locale.getLocaleComponentForUserAgent();
462
463 // See nsLanguageAtomService::GetLanguageGroup
464 let localeLangGroup = "x-unicode";
465 let bundle = Services.strings.createBundle(
466 "resource://gre/res/langGroups.properties");
467 try {
468 localeLangGroup = bundle.GetStringFromName(locale);
469 }
470 catch(ex) {
471 let hyphenAt = locale.indexOf("-");
472 if (hyphenAt != -1) {
473 try {
474 localeLangGroup = bundle.GetStringFromName(locale.substr(0, hyphenAt));
475 }
476 catch(ex2) { }
477 }
478 }
479 return localeLangGroup;
480 },
481
482 _migrateDownloadsFolder: function MPR__migrateDownloadsFolder() {
483 // Windows Safari uses DownloadPath while Mac uses DownloadsPath.
484 // Check both for future compatibility.
485 let key;
486 if (this._dict.has("DownloadsPath"))
487 key = "DownloadsPath";
488 else if (this._dict.has("DownloadPath"))
489 key = "DownloadPath";
490 else
491 return;
492
493 let downloadsFolder = FileUtils.File(this._dict.get(key));
494
495 // If the download folder is set to the Desktop or to ~/Downloads, set the
496 // folderList pref appropriately so that "Desktop"/Downloads is shown with
497 // pretty name in the preferences dialog.
498 let folderListVal = 2;
499 if (downloadsFolder.equals(FileUtils.getDir("Desk", []))) {
500 folderListVal = 0;
501 }
502 else {
503 let dnldMgr = Cc["@mozilla.org/download-manager;1"].
504 getService(Ci.nsIDownloadManager);
505 if (downloadsFolder.equals(dnldMgr.defaultDownloadsDirectory))
506 folderListVal = 1;
507 }
508 Services.prefs.setIntPref("browser.download.folderList", folderListVal);
509 Services.prefs.setComplexValue("browser.download.dir", Ci.nsILocalFile,
510 downloadsFolder);
511 }
512 };
513
514 function SearchStrings(aMainPreferencesPropertyListInstance) {
515 this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
516 }
517 SearchStrings.prototype = {
518 type: MigrationUtils.resourceTypes.OTHERDATA,
519
520 migrate: function SS_migrate(aCallback) {
521 this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
522 function migrateSearchStrings(aDict) {
523 if (!aDict)
524 throw new Error("Could not get preferences dictionary");
525
526 if (aDict.has("RecentSearchStrings")) {
527 let recentSearchStrings = aDict.get("RecentSearchStrings");
528 if (recentSearchStrings && recentSearchStrings.length > 0) {
529 let changes = [{op: "add",
530 fieldname: "searchbar-history",
531 value: searchString}
532 for (searchString of recentSearchStrings)];
533 FormHistory.update(changes);
534 }
535 }
536 }.bind(this), aCallback));
537 }
538 };
539
540 #ifdef XP_MACOSX
541 // On OS X, the cookie-accept policy preference is stored in a separate
542 // property list.
543 // For the Windows version, check Preferences.migrate.
544 function WebFoundationCookieBehavior(aWebFoundationFile) {
545 this._file = aWebFoundationFile;
546 }
547 WebFoundationCookieBehavior.prototype = {
548 type: MigrationUtils.resourceTypes.SETTINGS,
549
550 migrate: function WFPL_migrate(aCallback) {
551 PropertyListUtils.read(this._file, MigrationUtils.wrapMigrateFunction(
552 function migrateCookieBehavior(aDict) {
553 if (!aDict)
554 throw new Error("Could not read com.apple.WebFoundation.plist");
555
556 if (aDict.has("NSHTTPAcceptCookies")) {
557 // Setting Safari Firefox
558 // Always Accept always 0
559 // Accept from Originating current page 1
560 // Never Accept never 2
561 let acceptCookies = aDict.get("NSHTTPAcceptCookies");
562 let cookieValue = acceptCookies == "never" ? 2 :
563 acceptCookies == "current page" ? 1 : 0;
564 Services.prefs.setIntPref("network.cookie.cookieBehavior",
565 cookieValue);
566 }
567 }.bind(this), aCallback));
568 }
569 };
570 #endif
571
572 function SafariProfileMigrator() {
573 }
574
575 SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
576
577 SafariProfileMigrator.prototype.getResources = function SM_getResources() {
578 let profileDir =
579 #ifdef XP_MACOSX
580 FileUtils.getDir("ULibDir", ["Safari"], false);
581 #else
582 FileUtils.getDir("AppData", ["Apple Computer", "Safari"], false);
583 #endif
584 if (!profileDir.exists())
585 return null;
586
587 let resources = [];
588 let pushProfileFileResource = function(aFileName, aConstructor) {
589 let file = profileDir.clone();
590 file.append(aFileName);
591 if (file.exists())
592 resources.push(new aConstructor(file));
593 };
594
595 pushProfileFileResource("History.plist", History);
596 pushProfileFileResource("Bookmarks.plist", Bookmarks);
597
598 // The Reading List feature was introduced at the same time in Windows and
599 // Mac versions of Safari. Not surprisingly, they are stored in the same
600 // format in both versions. Surpsingly, only on Windows there is a
601 // separate property list for it. This isn't #ifdefed out on mac, because
602 // Apple may fix this at some point.
603 pushProfileFileResource("ReadingList.plist", Bookmarks);
604
605 let prefsDir =
606 #ifdef XP_MACOSX
607 FileUtils.getDir("UsrPrfs", [], false);
608 #else
609 FileUtils.getDir("AppData", ["Apple Computer", "Preferences"], false);
610 #endif
611
612 let prefs = this.mainPreferencesPropertyList;
613 if (prefs) {
614 resources.push(new Preferences(prefs));
615 resources.push(new SearchStrings(prefs));
616 }
617
618 #ifdef XP_MACOSX
619 // On OS X, the cookie-accept policy preference is stored in a separate
620 // property list.
621 let wfFile = FileUtils.getFile("UsrPrfs", ["com.apple.WebFoundation.plist"]);
622 if (wfFile.exists())
623 resources.push(new WebFoundationCookieBehavior(wfFile));
624 #endif
625
626 return resources;
627 };
628
629 Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", {
630 get: function get_mainPreferencesPropertyList() {
631 if (this._mainPreferencesPropertyList === undefined) {
632 let file =
633 #ifdef XP_MACOSX
634 FileUtils.getDir("UsrPrfs", [], false);
635 #else
636 FileUtils.getDir("AppData", ["Apple Computer", "Preferences"], false);
637 #endif
638 if (file.exists()) {
639 file.append("com.apple.Safari.plist");
640 if (file.exists()) {
641 return this._mainPreferencesPropertyList =
642 new MainPreferencesPropertyList(file);
643 }
644 }
645 return this._mainPreferencesPropertyList = null;
646 }
647 return this._mainPreferencesPropertyList;
648 }
649 });
650
651 Object.defineProperty(SafariProfileMigrator.prototype, "sourceHomePageURL", {
652 get: function get_sourceHomePageURL() {
653 if (this.mainPreferencesPropertyList) {
654 let dict = this.mainPreferencesPropertyList._readSync();
655 if (dict.has("HomePage"))
656 return dict.get("HomePage");
657 }
658 return "";
659 }
660 });
661
662 SafariProfileMigrator.prototype.classDescription = "Safari Profile Migrator";
663 SafariProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=safari";
664 SafariProfileMigrator.prototype.classID = Components.ID("{4b609ecf-60b2-4655-9df4-dc149e474da1}");
665
666 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SafariProfileMigrator]);

mercurial