summaryrefslogtreecommitdiffstats
path: root/browser/components/migration/SafariProfileMigrator.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/migration/SafariProfileMigrator.js')
-rw-r--r--browser/components/migration/SafariProfileMigrator.js650
1 files changed, 650 insertions, 0 deletions
diff --git a/browser/components/migration/SafariProfileMigrator.js b/browser/components/migration/SafariProfileMigrator.js
new file mode 100644
index 000000000..6a2dbfcb1
--- /dev/null
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -0,0 +1,650 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
+
+XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
+ "resource://gre/modules/Downloads.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PropertyListUtils",
+ "resource://gre/modules/PropertyListUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+ "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+ "resource://gre/modules/FormHistory.jsm");
+
+function Bookmarks(aBookmarksFile) {
+ this._file = aBookmarksFile;
+}
+Bookmarks.prototype = {
+ type: MigrationUtils.resourceTypes.BOOKMARKS,
+
+ migrate: function B_migrate(aCallback) {
+ return Task.spawn(function* () {
+ let dict = yield new Promise(resolve =>
+ PropertyListUtils.read(this._file, resolve)
+ );
+ if (!dict)
+ throw new Error("Could not read Bookmarks.plist");
+ let children = dict.get("Children");
+ if (!children)
+ throw new Error("Invalid Bookmarks.plist format");
+
+ let collection = dict.get("Title") == "com.apple.ReadingList" ?
+ this.READING_LIST_COLLECTION : this.ROOT_COLLECTION;
+ yield this._migrateCollection(children, collection);
+ }.bind(this)).then(() => aCallback(true),
+ e => { Cu.reportError(e); aCallback(false) });
+ },
+
+ // Bookmarks collections in Safari. Constants for migrateCollection.
+ ROOT_COLLECTION: 0,
+ MENU_COLLECTION: 1,
+ TOOLBAR_COLLECTION: 2,
+ READING_LIST_COLLECTION: 3,
+
+ /**
+ * Recursively migrate a Safari collection of bookmarks.
+ *
+ * @param aEntries
+ * the collection's children
+ * @param aCollection
+ * one of the values above.
+ */
+ _migrateCollection: Task.async(function* (aEntries, aCollection) {
+ // A collection of bookmarks in Safari resembles places roots. In the
+ // property list files (Bookmarks.plist, ReadingList.plist) they are
+ // stored as regular bookmarks folders, and thus can only be distinguished
+ // from by their names and places in the hierarchy.
+
+ let entriesFiltered = [];
+ if (aCollection == this.ROOT_COLLECTION) {
+ for (let entry of aEntries) {
+ let type = entry.get("WebBookmarkType");
+ if (type == "WebBookmarkTypeList" && entry.has("Children")) {
+ let title = entry.get("Title");
+ let children = entry.get("Children");
+ if (title == "BookmarksBar")
+ yield this._migrateCollection(children, this.TOOLBAR_COLLECTION);
+ else if (title == "BookmarksMenu")
+ yield this._migrateCollection(children, this.MENU_COLLECTION);
+ else if (title == "com.apple.ReadingList")
+ yield this._migrateCollection(children, this.READING_LIST_COLLECTION);
+ else if (entry.get("ShouldOmitFromUI") !== true)
+ entriesFiltered.push(entry);
+ }
+ else if (type == "WebBookmarkTypeLeaf") {
+ entriesFiltered.push(entry);
+ }
+ }
+ }
+ else {
+ entriesFiltered = aEntries;
+ }
+
+ if (entriesFiltered.length == 0)
+ return;
+
+ let folderGuid = -1;
+ switch (aCollection) {
+ case this.ROOT_COLLECTION: {
+ // In Safari, it is possible (though quite cumbersome) to move
+ // bookmarks to the bookmarks root, which is the parent folder of
+ // all bookmarks "collections". That is somewhat in parallel with
+ // both the places root and the unfiled-bookmarks root.
+ // Because the former is only an implementation detail in our UI,
+ // the unfiled root seems to be the best choice.
+ folderGuid = PlacesUtils.bookmarks.unfiledGuid;
+ break;
+ }
+ case this.MENU_COLLECTION: {
+ folderGuid = PlacesUtils.bookmarks.menuGuid;
+ if (!MigrationUtils.isStartupMigration) {
+ folderGuid =
+ yield MigrationUtils.createImportedBookmarksFolder("Safari", folderGuid);
+ }
+ break;
+ }
+ case this.TOOLBAR_COLLECTION: {
+ folderGuid = PlacesUtils.bookmarks.toolbarGuid;
+ if (!MigrationUtils.isStartupMigration) {
+ folderGuid =
+ yield MigrationUtils.createImportedBookmarksFolder("Safari", folderGuid);
+ }
+ break;
+ }
+ case this.READING_LIST_COLLECTION: {
+ // Reading list items are imported as regular bookmarks.
+ // They are imported under their own folder, created either under the
+ // bookmarks menu (in the case of startup migration).
+ folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
+ parentGuid: PlacesUtils.bookmarks.menuGuid,
+ type: PlacesUtils.bookmarks.TYPE_FOLDER,
+ title: MigrationUtils.getLocalizedString("importedSafariReadingList"),
+ })).guid;
+ break;
+ }
+ default:
+ throw new Error("Unexpected value for aCollection!");
+ }
+ if (folderGuid == -1)
+ throw new Error("Invalid folder GUID");
+
+ yield this._migrateEntries(entriesFiltered, folderGuid);
+ }),
+
+ // migrate the given array of safari bookmarks to the given places
+ // folder.
+ _migrateEntries: Task.async(function* (entries, parentGuid) {
+ for (let entry of entries) {
+ let type = entry.get("WebBookmarkType");
+ if (type == "WebBookmarkTypeList" && entry.has("Children")) {
+ let title = entry.get("Title");
+ let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
+ parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title
+ })).guid;
+
+ // Empty folders may not have a children array.
+ if (entry.has("Children"))
+ yield this._migrateEntries(entry.get("Children"), newFolderGuid, false);
+ }
+ else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
+ let title;
+ if (entry.has("URIDictionary"))
+ title = entry.get("URIDictionary").get("title");
+
+ try {
+ yield MigrationUtils.insertBookmarkWrapper({
+ parentGuid, url: entry.get("URLString"), title
+ });
+ } catch (ex) {
+ Cu.reportError("Invalid Safari bookmark: " + ex);
+ }
+ }
+ }
+ })
+};
+
+function History(aHistoryFile) {
+ this._file = aHistoryFile;
+}
+History.prototype = {
+ type: MigrationUtils.resourceTypes.HISTORY,
+
+ // Helper method for converting the visit date property to a PRTime value.
+ // The visit date is stored as a string, so it's not read as a Date
+ // object by PropertyListUtils.
+ _parseCocoaDate: function H___parseCocoaDate(aCocoaDateStr) {
+ let asDouble = parseFloat(aCocoaDateStr);
+ if (!isNaN(asDouble)) {
+ // reference date of NSDate.
+ let date = new Date("1 January 2001, GMT");
+ date.setMilliseconds(asDouble * 1000);
+ return date * 1000;
+ }
+ return 0;
+ },
+
+ migrate: function H_migrate(aCallback) {
+ PropertyListUtils.read(this._file, function migrateHistory(aDict) {
+ try {
+ if (!aDict)
+ throw new Error("Could not read history property list");
+ if (!aDict.has("WebHistoryDates"))
+ throw new Error("Unexpected history-property list format");
+
+ // Safari's History file contains only top-level urls. It does not
+ // distinguish between typed urls and linked urls.
+ let transType = PlacesUtils.history.TRANSITION_LINK;
+
+ let places = [];
+ let entries = aDict.get("WebHistoryDates");
+ for (let entry of entries) {
+ if (entry.has("lastVisitedDate")) {
+ let visitDate = this._parseCocoaDate(entry.get("lastVisitedDate"));
+ try {
+ places.push({ uri: NetUtil.newURI(entry.get("")),
+ title: entry.get("title"),
+ visits: [{ transitionType: transType,
+ visitDate: visitDate }] });
+ }
+ catch (ex) {
+ // Safari's History file may contain malformed URIs which
+ // will be ignored.
+ Cu.reportError(ex);
+ }
+ }
+ }
+ if (places.length > 0) {
+ MigrationUtils.insertVisitsWrapper(places, {
+ _success: false,
+ handleResult: function() {
+ // Importing any entry is considered a successful import.
+ this._success = true;
+ },
+ handleError: function() {},
+ handleCompletion: function() {
+ aCallback(this._success);
+ }
+ });
+ }
+ else {
+ aCallback(false);
+ }
+ }
+ catch (ex) {
+ Cu.reportError(ex);
+ aCallback(false);
+ }
+ }.bind(this));
+ }
+};
+
+/**
+ * Safari's preferences property list is independently used for three purposes:
+ * (a) importation of preferences
+ * (b) importation of search strings
+ * (c) retrieving the home page.
+ *
+ * So, rather than reading it three times, it's cached and managed here.
+ */
+function MainPreferencesPropertyList(aPreferencesFile) {
+ this._file = aPreferencesFile;
+ this._callbacks = [];
+}
+MainPreferencesPropertyList.prototype = {
+ /**
+ * @see PropertyListUtils.read
+ */
+ read: function MPPL_read(aCallback) {
+ if ("_dict" in this) {
+ aCallback(this._dict);
+ return;
+ }
+
+ let alreadyReading = this._callbacks.length > 0;
+ this._callbacks.push(aCallback);
+ if (!alreadyReading) {
+ PropertyListUtils.read(this._file, function readPrefs(aDict) {
+ this._dict = aDict;
+ for (let callback of this._callbacks) {
+ try {
+ callback(aDict);
+ }
+ catch (ex) {
+ Cu.reportError(ex);
+ }
+ }
+ this._callbacks.splice(0);
+ }.bind(this));
+ }
+ },
+
+ // Workaround for nsIBrowserProfileMigrator.sourceHomePageURL until
+ // it's replaced with an async method.
+ _readSync: function MPPL__readSync() {
+ if ("_dict" in this)
+ return this._dict;
+
+ let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Ci.nsIFileInputStream);
+ inputStream.init(this._file, -1, -1, 0);
+ let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(Ci.nsIBinaryInputStream);
+ binaryStream.setInputStream(inputStream);
+ let bytes = binaryStream.readByteArray(inputStream.available());
+ this._dict = PropertyListUtils._readFromArrayBufferSync(
+ new Uint8Array(bytes).buffer);
+ return this._dict;
+ }
+};
+
+function Preferences(aMainPreferencesPropertyListInstance) {
+ this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
+}
+Preferences.prototype = {
+ type: MigrationUtils.resourceTypes.SETTINGS,
+
+ migrate: function MPR_migrate(aCallback) {
+ this._mainPreferencesPropertyList.read(aDict => {
+ Task.spawn(function* () {
+ if (!aDict)
+ throw new Error("Could not read preferences file");
+
+ this._dict = aDict;
+
+ let invert = webkitVal => !webkitVal;
+ this._set("AutoFillPasswords", "signon.rememberSignons");
+ this._set("OpenNewTabsInFront", "browser.tabs.loadInBackground", invert);
+ this._set("WebKitJavaScriptCanOpenWindowsAutomatically",
+ "dom.disable_open_during_load", invert);
+
+ // layout.spellcheckDefault is a boolean stored as a number.
+ this._set("WebContinuousSpellCheckingEnabled",
+ "layout.spellcheckDefault", Number);
+
+ // Auto-load images
+ // Firefox has an elaborate set of Image preferences. The correlation is:
+ // Mode: Safari Firefox
+ // Blocked FALSE 2
+ // Allowed TRUE 1
+ // Allowed, originating site only -- 3
+ this._set("WebKitDisplayImagesKey", "permissions.default.image",
+ webkitVal => webkitVal ? 1 : 2);
+
+ this._migrateFontSettings();
+ yield this._migrateDownloadsFolder();
+ }.bind(this)).then(() => aCallback(true), ex => {
+ Cu.reportError(ex);
+ aCallback(false);
+ }).catch(Cu.reportError);
+ });
+ },
+
+ /**
+ * Attempts to migrates a preference from Safari. Returns whether the preference
+ * has been migrated.
+ * @param aSafariKey
+ * The dictionary key for the preference of Safari.
+ * @param aMozPref
+ * The gecko/firefox preference to which aSafariKey should be migrated
+ * @param [optional] aConvertFunction(aSafariValue)
+ * a function that converts the safari-preference value to the
+ * appropriate value for aMozPref. If it's not passed, then the
+ * Safari value is set as is.
+ * If aConvertFunction returns undefined, then aMozPref is not set
+ * at all.
+ * @return whether or not aMozPref was set.
+ */
+ _set: function MPR_set(aSafariKey, aMozPref, aConvertFunction) {
+ if (this._dict.has(aSafariKey)) {
+ let safariVal = this._dict.get(aSafariKey);
+ let mozVal = aConvertFunction !== undefined ?
+ aConvertFunction(safariVal) : safariVal;
+ switch (typeof mozVal) {
+ case "string":
+ Services.prefs.setCharPref(aMozPref, mozVal);
+ break;
+ case "number":
+ Services.prefs.setIntPref(aMozPref, mozVal);
+ break;
+ case "boolean":
+ Services.prefs.setBoolPref(aMozPref, mozVal);
+ break;
+ case "undefined":
+ return false;
+ default:
+ throw new Error("Unexpected value type: " + (typeof mozVal));
+ }
+ }
+ return true;
+ },
+
+ // Fonts settings are quite problematic for migration, for a couple of
+ // reasons:
+ // (a) Every font preference in Gecko is set for a particular language.
+ // In Safari, each font preference applies to all languages.
+ // (b) The current underlying implementation of nsIFontEnumerator cannot
+ // really tell you anything about a font: no matter what language or type
+ // you try to enumerate with EnumerateFonts, you get an array of all
+ // fonts in the systems (This also breaks our fonts dialog).
+ // (c) In Gecko, each langauge has a distinct serif and sans-serif font
+ // preference. Safari has only one default font setting. It seems that
+ // it checks if it's a serif or sans serif font, and when a site
+ // explicitly asks to use serif/sans-serif font, it uses the default font
+ // only if it applies to this type.
+ // (d) The solution of guessing the lang-group out of the default charset (as
+ // done in the old Safari migrator) can only work when:
+ // (1) The default charset preference is set.
+ // (2) It's not a unicode charset.
+ // For now, we use the language implied by the system locale as the
+ // lang-group. The only exception is minimal font size, which is an
+ // accessibility preference in Safari (under the Advanced tab). If it is set,
+ // we set it for all languages.
+ // As for the font type of the default font (serif/sans-serif), the default
+ // type for the given language is used (set in font.default.LANGGROUP).
+ _migrateFontSettings: function MPR__migrateFontSettings() {
+ // If "Never use font sizes smaller than [ ] is set", migrate it for all
+ // languages.
+ if (this._dict.has("WebKitMinimumFontSize")) {
+ let minimumSize = this._dict.get("WebKitMinimumFontSize");
+ if (typeof minimumSize == "number") {
+ let prefs = Services.prefs.getChildList("font.minimum-size");
+ for (let pref of prefs) {
+ Services.prefs.setIntPref(pref, minimumSize);
+ }
+ }
+ else {
+ Cu.reportError("WebKitMinimumFontSize was set to an invalid value: " +
+ minimumSize);
+ }
+ }
+
+ // In theory, the lang group could be "x-unicode". This will result
+ // in setting the fonts for "Other Languages".
+ let lang = this._getLocaleLangGroup();
+
+ let anySet = false;
+ let fontType = Services.prefs.getCharPref("font.default." + lang);
+ anySet |= this._set("WebKitFixedFont", "font.name.monospace." + lang);
+ anySet |= this._set("WebKitDefaultFixedFontSize", "font.size.fixed." + lang);
+ anySet |= this._set("WebKitStandardFont",
+ "font.name." + fontType + "." + lang);
+ anySet |= this._set("WebKitDefaultFontSize", "font.size.variable." + lang);
+
+ // If we set font settings for a particular language, we'll also set the
+ // fonts dialog to open with the fonts settings for that langauge.
+ if (anySet)
+ Services.prefs.setCharPref("font.language.group", lang);
+ },
+
+ // Get the language group for the system locale.
+ _getLocaleLangGroup: function MPR__getLocaleLangGroup() {
+ let locale = Services.locale.getLocaleComponentForUserAgent();
+
+ // See nsLanguageAtomService::GetLanguageGroup
+ let localeLangGroup = "x-unicode";
+ let bundle = Services.strings.createBundle(
+ "resource://gre/res/langGroups.properties");
+ try {
+ localeLangGroup = bundle.GetStringFromName(locale);
+ }
+ catch (ex) {
+ let hyphenAt = locale.indexOf("-");
+ if (hyphenAt != -1) {
+ try {
+ localeLangGroup = bundle.GetStringFromName(locale.substr(0, hyphenAt));
+ }
+ catch (ex2) { }
+ }
+ }
+ return localeLangGroup;
+ },
+
+ _migrateDownloadsFolder: Task.async(function* () {
+ if (!this._dict.has("DownloadsPath"))
+ return;
+
+ let downloadsFolder = FileUtils.File(this._dict.get("DownloadsPath"));
+
+ // If the download folder is set to the Desktop or to ~/Downloads, set the
+ // folderList pref appropriately so that "Desktop"/Downloads is shown with
+ // pretty name in the preferences dialog.
+ let folderListVal = 2;
+ if (downloadsFolder.equals(FileUtils.getDir("Desk", []))) {
+ folderListVal = 0;
+ }
+ else {
+ let systemDownloadsPath = yield Downloads.getSystemDownloadsDirectory();
+ let systemDownloadsFolder = FileUtils.File(systemDownloadsPath);
+ if (downloadsFolder.equals(systemDownloadsFolder))
+ folderListVal = 1;
+ }
+ Services.prefs.setIntPref("browser.download.folderList", folderListVal);
+ Services.prefs.setComplexValue("browser.download.dir", Ci.nsILocalFile,
+ downloadsFolder);
+ }),
+};
+
+function SearchStrings(aMainPreferencesPropertyListInstance) {
+ this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
+}
+SearchStrings.prototype = {
+ type: MigrationUtils.resourceTypes.OTHERDATA,
+
+ migrate: function SS_migrate(aCallback) {
+ this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
+ function migrateSearchStrings(aDict) {
+ if (!aDict)
+ throw new Error("Could not get preferences dictionary");
+
+ if (aDict.has("RecentSearchStrings")) {
+ let recentSearchStrings = aDict.get("RecentSearchStrings");
+ if (recentSearchStrings && recentSearchStrings.length > 0) {
+ let changes = recentSearchStrings.map((searchString) => (
+ {op: "add",
+ fieldname: "searchbar-history",
+ value: searchString}));
+ FormHistory.update(changes);
+ }
+ }
+ }.bind(this), aCallback));
+ }
+};
+
+// On OS X, the cookie-accept policy preference is stored in a separate
+// property list.
+function WebFoundationCookieBehavior(aWebFoundationFile) {
+ this._file = aWebFoundationFile;
+}
+WebFoundationCookieBehavior.prototype = {
+ type: MigrationUtils.resourceTypes.SETTINGS,
+
+ migrate: function WFPL_migrate(aCallback) {
+ PropertyListUtils.read(this._file, MigrationUtils.wrapMigrateFunction(
+ function migrateCookieBehavior(aDict) {
+ if (!aDict)
+ throw new Error("Could not read com.apple.WebFoundation.plist");
+
+ if (aDict.has("NSHTTPAcceptCookies")) {
+ // Setting Safari Firefox
+ // Always Accept always 0
+ // Accept from Originating current page 1
+ // Never Accept never 2
+ let acceptCookies = aDict.get("NSHTTPAcceptCookies");
+ let cookieValue = 0;
+ if (acceptCookies == "never") {
+ cookieValue = 2;
+ } else if (acceptCookies == "current page") {
+ cookieValue = 1;
+ }
+ Services.prefs.setIntPref("network.cookie.cookieBehavior",
+ cookieValue);
+ }
+ }.bind(this), aCallback));
+ }
+};
+
+function SafariProfileMigrator() {
+}
+
+SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
+
+SafariProfileMigrator.prototype.getResources = function SM_getResources() {
+ let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
+ if (!profileDir.exists())
+ return null;
+
+ let resources = [];
+ let pushProfileFileResource = function(aFileName, aConstructor) {
+ let file = profileDir.clone();
+ file.append(aFileName);
+ if (file.exists())
+ resources.push(new aConstructor(file));
+ };
+
+ pushProfileFileResource("History.plist", History);
+ pushProfileFileResource("Bookmarks.plist", Bookmarks);
+
+ // The Reading List feature was introduced at the same time in Windows and
+ // Mac versions of Safari. Not surprisingly, they are stored in the same
+ // format in both versions. Surpsingly, only on Windows there is a
+ // separate property list for it. This code is used on mac too, because
+ // Apple may fix this at some point.
+ pushProfileFileResource("ReadingList.plist", Bookmarks);
+
+ let prefs = this.mainPreferencesPropertyList;
+ if (prefs) {
+ resources.push(new Preferences(prefs));
+ resources.push(new SearchStrings(prefs));
+ }
+
+ let wfFile = FileUtils.getFile("UsrPrfs", ["com.apple.WebFoundation.plist"]);
+ if (wfFile.exists())
+ resources.push(new WebFoundationCookieBehavior(wfFile));
+
+ return resources;
+};
+
+SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate() {
+ let profileDir = FileUtils.getDir("ULibDir", ["Safari"], false);
+ let datePromises = ["Bookmarks.plist", "History.plist"].map(file => {
+ let path = OS.Path.join(profileDir.path, file);
+ return OS.File.stat(path).catch(() => null).then(info => {
+ return info ? info.lastModificationDate : 0;
+ });
+ });
+ return Promise.all(datePromises).then(dates => {
+ return new Date(Math.max.apply(Math, dates));
+ });
+};
+
+Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", {
+ get: function get_mainPreferencesPropertyList() {
+ if (this._mainPreferencesPropertyList === undefined) {
+ let file = FileUtils.getDir("UsrPrfs", [], false);
+ if (file.exists()) {
+ file.append("com.apple.Safari.plist");
+ if (file.exists()) {
+ this._mainPreferencesPropertyList =
+ new MainPreferencesPropertyList(file);
+ return this._mainPreferencesPropertyList;
+ }
+ }
+ this._mainPreferencesPropertyList = null;
+ return this._mainPreferencesPropertyList;
+ }
+ return this._mainPreferencesPropertyList;
+ }
+});
+
+Object.defineProperty(SafariProfileMigrator.prototype, "sourceHomePageURL", {
+ get: function get_sourceHomePageURL() {
+ if (this.mainPreferencesPropertyList) {
+ let dict = this.mainPreferencesPropertyList._readSync();
+ if (dict.has("HomePage"))
+ return dict.get("HomePage");
+ }
+ return "";
+ }
+});
+
+SafariProfileMigrator.prototype.classDescription = "Safari Profile Migrator";
+SafariProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=safari";
+SafariProfileMigrator.prototype.classID = Components.ID("{4b609ecf-60b2-4655-9df4-dc149e474da1}");
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SafariProfileMigrator]);