diff options
Diffstat (limited to 'browser/components/migration/SafariProfileMigrator.js')
-rw-r--r-- | browser/components/migration/SafariProfileMigrator.js | 650 |
1 files changed, 0 insertions, 650 deletions
diff --git a/browser/components/migration/SafariProfileMigrator.js b/browser/components/migration/SafariProfileMigrator.js deleted file mode 100644 index 6a2dbfcb1..000000000 --- a/browser/components/migration/SafariProfileMigrator.js +++ /dev/null @@ -1,650 +0,0 @@ -/* 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]); |