browser/components/migration/src/SafariProfileMigrator.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/components/migration/src/SafariProfileMigrator.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,666 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +let Cc = Components.classes;
    1.11 +let Ci = Components.interfaces;
    1.12 +let Cu = Components.utils;
    1.13 +
    1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.15 +Cu.import("resource://gre/modules/FileUtils.jsm");
    1.16 +Cu.import("resource://gre/modules/Services.jsm");
    1.17 +Cu.import("resource:///modules/MigrationUtils.jsm");
    1.18 +
    1.19 +XPCOMUtils.defineLazyModuleGetter(this, "PropertyListUtils",
    1.20 +                                  "resource://gre/modules/PropertyListUtils.jsm");
    1.21 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
    1.22 +                                  "resource://gre/modules/PlacesUtils.jsm");
    1.23 +XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
    1.24 +                                  "resource://gre/modules/NetUtil.jsm");
    1.25 +XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
    1.26 +                                  "resource://gre/modules/FormHistory.jsm");
    1.27 +
    1.28 +function Bookmarks(aBookmarksFile) {
    1.29 +  this._file = aBookmarksFile;
    1.30 +}
    1.31 +Bookmarks.prototype = {
    1.32 +  type: MigrationUtils.resourceTypes.BOOKMARKS,
    1.33 +
    1.34 +  migrate: function B_migrate(aCallback) {
    1.35 +    PropertyListUtils.read(this._file,
    1.36 +      MigrationUtils.wrapMigrateFunction(function migrateBookmarks(aDict) {
    1.37 +        if (!aDict)
    1.38 +          throw new Error("Could not read Bookmarks.plist");
    1.39 +
    1.40 +        let children = aDict.get("Children");;
    1.41 +        if (!children)
    1.42 +          throw new Error("Invalid Bookmarks.plist format");
    1.43 +
    1.44 +        PlacesUtils.bookmarks.runInBatchMode({
    1.45 +          runBatched: function() {
    1.46 +            let collection = aDict.get("Title") == "com.apple.ReadingList" ?
    1.47 +              this.READING_LIST_COLLECTION : this.ROOT_COLLECTION;
    1.48 +            this._migrateCollection(children, collection);
    1.49 +          }.bind(this)
    1.50 +        }, null);
    1.51 +      }.bind(this), aCallback));
    1.52 +  },
    1.53 +
    1.54 +  // Bookmarks collections in Safari.  Constants for migrateCollection.
    1.55 +  ROOT_COLLECTION:         0,
    1.56 +  MENU_COLLECTION:         1,
    1.57 +  TOOLBAR_COLLECTION:      2,
    1.58 +  READING_LIST_COLLECTION: 3,
    1.59 +
    1.60 +  /**
    1.61 +   * Recursively migrate a Safari collection of bookmarks.
    1.62 +   *
    1.63 +   * @param aEntries
    1.64 +   *        the collection's children
    1.65 +   * @param aCollection
    1.66 +   *        one of the values above.
    1.67 +   */
    1.68 +  _migrateCollection: function B__migrateCollection(aEntries, aCollection) {
    1.69 +    // A collection of bookmarks in Safari resembles places roots.  In the
    1.70 +    // property list files (Bookmarks.plist, ReadingList.plist) they are
    1.71 +    // stored as regular bookmarks folders, and thus can only be distinguished
    1.72 +    // from by their names and places in the hierarchy.
    1.73 +
    1.74 +    let entriesFiltered = [];
    1.75 +    if (aCollection == this.ROOT_COLLECTION) {
    1.76 +      for (let entry of aEntries) {
    1.77 +        let type = entry.get("WebBookmarkType");
    1.78 +        if (type == "WebBookmarkTypeList" && entry.has("Children")) {
    1.79 +          let title = entry.get("Title");
    1.80 +          let children = entry.get("Children");
    1.81 +          if (title == "BookmarksBar")
    1.82 +            this._migrateCollection(children, this.TOOLBAR_COLLECTION);
    1.83 +          else if (title == "BookmarksMenu")
    1.84 +            this._migrateCollection(children, this.MENU_COLLECTION);
    1.85 +          else if (title == "com.apple.ReadingList")
    1.86 +            this._migrateCollection(children, this.READING_LIST_COLLECTION);
    1.87 +          else if (entry.get("ShouldOmitFromUI") !== true)
    1.88 +            entriesFiltered.push(entry);
    1.89 +        }
    1.90 +        else if (type == "WebBookmarkTypeLeaf") {
    1.91 +          entriesFiltered.push(entry);
    1.92 +        }
    1.93 +      }
    1.94 +    }
    1.95 +    else {
    1.96 +      entriesFiltered = aEntries;
    1.97 +    }
    1.98 +
    1.99 +    if (entriesFiltered.length == 0)
   1.100 +      return;
   1.101 +
   1.102 +    let folder = -1;
   1.103 +    switch (aCollection) {
   1.104 +      case this.ROOT_COLLECTION: {
   1.105 +        // In Safari, it is possible (though quite cumbersome) to move
   1.106 +        // bookmarks to the bookmarks root, which is the parent folder of
   1.107 +        // all bookmarks "collections".  That is somewhat in parallel with
   1.108 +        // both the places root and the unfiled-bookmarks root. 
   1.109 +        // Because the former is only an implementation detail in our UI,
   1.110 +        // the unfiled root seems to be the best choice.
   1.111 +        folder = PlacesUtils.unfiledBookmarksFolderId;
   1.112 +        break;
   1.113 +      }
   1.114 +      case this.MENU_COLLECTION: {
   1.115 +        folder = PlacesUtils.bookmarksMenuFolderId;
   1.116 +        if (!MigrationUtils.isStartupMigration) {
   1.117 +          folder = MigrationUtils.createImportedBookmarksFolder("Safari",
   1.118 +                                                                folder);
   1.119 +        }
   1.120 +        break;
   1.121 +      }
   1.122 +      case this.TOOLBAR_COLLECTION: {
   1.123 +        folder = PlacesUtils.toolbarFolderId;
   1.124 +        if (!MigrationUtils.isStartupMigration) {
   1.125 +          folder = MigrationUtils.createImportedBookmarksFolder("Safari",
   1.126 +                                                                folder);
   1.127 +        }
   1.128 +        break;
   1.129 +      }
   1.130 +      case this.READING_LIST_COLLECTION: {
   1.131 +        // Reading list items are imported as regular bookmarks.
   1.132 +        // They are imported under their own folder, created either under the
   1.133 +        // bookmarks menu (in the case of startup migration).
   1.134 +        folder = PlacesUtils.bookmarks.createFolder(
   1.135 +            PlacesUtils.bookmarksMenuFolderId,
   1.136 +            MigrationUtils.getLocalizedString("importedSafariReadingList"),
   1.137 +            PlacesUtils.bookmarks.DEFAULT_INDEX);
   1.138 +        break;
   1.139 +      }
   1.140 +      default:
   1.141 +        throw new Error("Unexpected value for aCollection!");
   1.142 +    }
   1.143 +
   1.144 +    this._migrateEntries(entriesFiltered, folder);
   1.145 +  },
   1.146 +
   1.147 +  // migrate the given array of safari bookmarks to the given places
   1.148 +  // folder.
   1.149 +  _migrateEntries: function B__migrateEntries(aEntries, aFolderId) {
   1.150 +    for (let entry of aEntries) {
   1.151 +      let type = entry.get("WebBookmarkType");
   1.152 +      if (type == "WebBookmarkTypeList" && entry.has("Children")) {
   1.153 +        let title = entry.get("Title");
   1.154 +        let folderId = PlacesUtils.bookmarks.createFolder(
   1.155 +           aFolderId, title, PlacesUtils.bookmarks.DEFAULT_INDEX);
   1.156 +
   1.157 +        // Empty folders may not have a children array.
   1.158 +        if (entry.has("Children"))
   1.159 +          this._migrateEntries(entry.get("Children"), folderId, false);
   1.160 +      }
   1.161 +      else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
   1.162 +        let title, uri;
   1.163 +        if (entry.has("URIDictionary"))
   1.164 +          title = entry.get("URIDictionary").get("title");
   1.165 +
   1.166 +        try {
   1.167 +          uri = NetUtil.newURI(entry.get("URLString"));
   1.168 +        }
   1.169 +        catch(ex) {
   1.170 +          Cu.reportError("Invalid uri set for Safari bookmark: " + entry.get("URLString"));
   1.171 +        }
   1.172 +        if (uri) {
   1.173 +          PlacesUtils.bookmarks.insertBookmark(aFolderId, uri,
   1.174 +            PlacesUtils.bookmarks.DEFAULT_INDEX, title);
   1.175 +        }
   1.176 +      }
   1.177 +    }
   1.178 +  }
   1.179 +};
   1.180 +
   1.181 +function History(aHistoryFile) {
   1.182 +  this._file = aHistoryFile;
   1.183 +}
   1.184 +History.prototype = {
   1.185 +  type: MigrationUtils.resourceTypes.HISTORY,
   1.186 +
   1.187 +  // Helper method for converting the visit date property to a PRTime value.
   1.188 +  // The visit date is stored as a string, so it's not read as a Date
   1.189 +  // object by PropertyListUtils.
   1.190 +  _parseCocoaDate: function H___parseCocoaDate(aCocoaDateStr) {
   1.191 +    let asDouble = parseFloat(aCocoaDateStr);
   1.192 +    if (!isNaN(asDouble)) {
   1.193 +      // reference date of NSDate.
   1.194 +      let date = new Date("1 January 2001, GMT");
   1.195 +      date.setMilliseconds(asDouble * 1000);
   1.196 +      return date * 1000;
   1.197 +    }
   1.198 +    return 0;
   1.199 +  },
   1.200 +
   1.201 +  migrate: function H_migrate(aCallback) {
   1.202 +    PropertyListUtils.read(this._file, function migrateHistory(aDict) {
   1.203 +      try {
   1.204 +        if (!aDict)
   1.205 +          throw new Error("Could not read history property list");
   1.206 +        if (!aDict.has("WebHistoryDates"))
   1.207 +          throw new Error("Unexpected history-property list format");
   1.208 +
   1.209 +        // Safari's History file contains only top-level urls.  It does not
   1.210 +        // distinguish between typed urls and linked urls.
   1.211 +        let transType = PlacesUtils.history.TRANSITION_LINK;
   1.212 +
   1.213 +        let places = [];
   1.214 +        let entries = aDict.get("WebHistoryDates");
   1.215 +        for (let entry of entries) {
   1.216 +          if (entry.has("lastVisitedDate")) {
   1.217 +            let visitDate = this._parseCocoaDate(entry.get("lastVisitedDate"));
   1.218 +            try {
   1.219 +              places.push({ uri: NetUtil.newURI(entry.get("")),
   1.220 +                            title: entry.get("title"),
   1.221 +                            visits: [{ transitionType: transType,
   1.222 +                                       visitDate: visitDate }] });
   1.223 +            }
   1.224 +            catch(ex) {
   1.225 +              // Safari's History file may contain malformed URIs which
   1.226 +              // will be ignored.
   1.227 +              Cu.reportError(ex)
   1.228 +            }
   1.229 +          }
   1.230 +        }
   1.231 +        if (places.length > 0) {
   1.232 +          PlacesUtils.asyncHistory.updatePlaces(places, {
   1.233 +            _success: false,
   1.234 +            handleResult: function() {
   1.235 +              // Importing any entry is considered a successful import.
   1.236 +              this._success = true;
   1.237 +            },
   1.238 +            handleError: function() {},
   1.239 +            handleCompletion: function() {
   1.240 +              aCallback(this._success);
   1.241 +            }
   1.242 +          });
   1.243 +        }
   1.244 +        else {
   1.245 +          aCallback(false);
   1.246 +        }
   1.247 +      }
   1.248 +      catch(ex) {
   1.249 +        Cu.reportError(ex);
   1.250 +        aCallback(false);
   1.251 +      }
   1.252 +    }.bind(this));
   1.253 +  }
   1.254 +};
   1.255 +
   1.256 +/**
   1.257 + * Safari's preferences property list is independently used for three purposes:
   1.258 + * (a) importation of preferences
   1.259 + * (b) importation of search strings
   1.260 + * (c) retrieving the home page.
   1.261 + *
   1.262 + * So, rather than reading it three times, it's cached and managed here.
   1.263 + */
   1.264 +function MainPreferencesPropertyList(aPreferencesFile) {
   1.265 +  this._file = aPreferencesFile;
   1.266 +  this._callbacks = [];
   1.267 +}
   1.268 +MainPreferencesPropertyList.prototype = {
   1.269 +  /**
   1.270 +   * @see PropertyListUtils.read
   1.271 +   */
   1.272 +  read: function MPPL_read(aCallback) {
   1.273 +    if ("_dict" in this) {
   1.274 +      aCallback(this._dict);
   1.275 +      return;
   1.276 +    }
   1.277 +
   1.278 +    let alreadyReading = this._callbacks.length > 0;
   1.279 +    this._callbacks.push(aCallback);
   1.280 +    if (!alreadyReading) {
   1.281 +      PropertyListUtils.read(this._file, function readPrefs(aDict) {
   1.282 +        this._dict = aDict;
   1.283 +        for (let callback of this._callbacks) {
   1.284 +          try {
   1.285 +            callback(aDict);
   1.286 +          }
   1.287 +          catch(ex) {
   1.288 +            Cu.reportError(ex);
   1.289 +          }
   1.290 +        }
   1.291 +        this._callbacks.splice(0);
   1.292 +      }.bind(this));
   1.293 +    }
   1.294 +  },
   1.295 +
   1.296 +  // Workaround for nsIBrowserProfileMigrator.sourceHomePageURL until
   1.297 +  // it's replaced with an async method.
   1.298 +  _readSync: function MPPL__readSync() {
   1.299 +    if ("_dict" in this)
   1.300 +      return this._dict;
   1.301 +  
   1.302 +    let inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
   1.303 +                      createInstance(Ci.nsIFileInputStream);
   1.304 +    inputStream.init(this._file, -1, -1, 0);
   1.305 +    let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
   1.306 +                       createInstance(Ci.nsIBinaryInputStream);
   1.307 +    binaryStream.setInputStream(inputStream);
   1.308 +    let bytes = binaryStream.readByteArray(inputStream.available());
   1.309 +    this._dict = PropertyListUtils._readFromArrayBufferSync(
   1.310 +      new Uint8Array(bytes).buffer);
   1.311 +    return this._dict;
   1.312 +  }
   1.313 +};
   1.314 +
   1.315 +function Preferences(aMainPreferencesPropertyListInstance) {
   1.316 +  this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
   1.317 +}
   1.318 +Preferences.prototype = {
   1.319 +  type: MigrationUtils.resourceTypes.SETTINGS,
   1.320 +
   1.321 +  migrate: function MPR_migrate(aCallback) {
   1.322 +    this._mainPreferencesPropertyList.read(
   1.323 +      MigrationUtils.wrapMigrateFunction(function migratePrefs(aDict) {
   1.324 +        if (!aDict)
   1.325 +          throw new Error("Could not read preferences file");
   1.326 +
   1.327 +        this._dict = aDict;
   1.328 +
   1.329 +        let invert = function(webkitVal) !webkitVal;
   1.330 +        this._set("AutoFillPasswords", "signon.rememberSignons");
   1.331 +        this._set("OpenNewTabsInFront", "browser.tabs.loadInBackground", invert);
   1.332 +        this._set("WebKitJavaScriptCanOpenWindowsAutomatically",
   1.333 +                   "dom.disable_open_during_load", invert);
   1.334 +
   1.335 +        // layout.spellcheckDefault is a boolean stored as a number.
   1.336 +        this._set("WebContinuousSpellCheckingEnabled",
   1.337 +                  "layout.spellcheckDefault", Number);
   1.338 +
   1.339 +        // Auto-load images
   1.340 +        // Firefox has an elaborate set of Image preferences. The correlation is:
   1.341 +        // Mode:                            Safari    Firefox
   1.342 +        // Blocked                          FALSE     2
   1.343 +        // Allowed                          TRUE      1
   1.344 +        // Allowed, originating site only   --        3
   1.345 +        this._set("WebKitDisplayImagesKey", "permissions.default.image",
   1.346 +                  function(webkitVal) webkitVal ? 1 : 2);
   1.347 +
   1.348 +#ifdef XP_WIN
   1.349 +        // Cookie-accept policy.
   1.350 +        // For the OS X version, see WebFoundationCookieBehavior.
   1.351 +        // Setting                    Safari          Firefox
   1.352 +        // Always Accept              0               0
   1.353 +        // Accept from Originating    2               1
   1.354 +        // Never Accept               1               2
   1.355 +        this._set("WebKitCookieStorageAcceptPolicy",
   1.356 +          "network.cookie.cookieBehavior",
   1.357 +          function(webkitVal) webkitVal == 0 ? 0 : webkitVal == 1 ? 2 : 1);
   1.358 +#endif
   1.359 +
   1.360 +        this._migrateFontSettings();
   1.361 +        this._migrateDownloadsFolder();
   1.362 +    }.bind(this), aCallback));
   1.363 +  },
   1.364 +
   1.365 +  /**
   1.366 +   * Attempts to migrates a preference from Safari.  Returns whether the preference
   1.367 +   * has been migrated.
   1.368 +   * @param aSafariKey
   1.369 +   *        The dictionary key for the preference of Safari.
   1.370 +   * @param aMozPref
   1.371 +   *        The gecko/firefox preference to which aSafariKey should be migrated
   1.372 +   * @param [optional] aConvertFunction(aSafariValue)
   1.373 +   *        a function that converts the safari-preference value to the
   1.374 +   *        appropriate value for aMozPref.  If it's not passed, then the
   1.375 +   *        Safari value is set as is.
   1.376 +   *        If aConvertFunction returns undefined, then aMozPref is not set
   1.377 +   *        at all.
   1.378 +   * @return whether or not aMozPref was set.
   1.379 +   */
   1.380 +  _set: function MPR_set(aSafariKey, aMozPref, aConvertFunction) {
   1.381 +    if (this._dict.has(aSafariKey)) {
   1.382 +      let safariVal = this._dict.get(aSafariKey);
   1.383 +      let mozVal = aConvertFunction !== undefined ?
   1.384 +                   aConvertFunction(safariVal) : safariVal;
   1.385 +      switch (typeof(mozVal)) {
   1.386 +        case "string":
   1.387 +          Services.prefs.setCharPref(aMozPref, mozVal);
   1.388 +          break;
   1.389 +        case "number":
   1.390 +          Services.prefs.setIntPref(aMozPref, mozVal);
   1.391 +          break;
   1.392 +        case "boolean":
   1.393 +          Services.prefs.setBoolPref(aMozPref, mozVal);
   1.394 +          break;
   1.395 +        case "undefined":
   1.396 +          return false;
   1.397 +        default:
   1.398 +          throw new Error("Unexpected value type: " + typeof(mozVal));
   1.399 +      }
   1.400 +    }
   1.401 +    return true;
   1.402 +  },
   1.403 +
   1.404 +  // Fonts settings are quite problematic for migration, for a couple of
   1.405 +  // reasons:
   1.406 +  // (a) Every font preference in Gecko is set for a particular language.
   1.407 +  //     In Safari, each font preference applies to all languages.
   1.408 +  // (b) The current underlying implementation of nsIFontEnumerator cannot
   1.409 +  //     really tell you anything about a font: no matter what language or type
   1.410 +  //     you try to enumerate with EnumerateFonts, you get an array of all
   1.411 +  //     fonts in the systems (This also breaks our fonts dialog).
   1.412 +  // (c) In Gecko, each langauge has a distinct serif and sans-serif font
   1.413 +  //     preference.  Safari has only one default font setting.  It seems that
   1.414 +  //     it checks if it's a serif or sans serif font, and when a site
   1.415 +  //     explicitly asks to use serif/sans-serif font, it uses the default font
   1.416 +  //     only if it applies to this type.
   1.417 +  // (d) The solution of guessing the lang-group out of the default charset (as
   1.418 +  //     done in the old Safari migrator) can only work when:
   1.419 +  //     (1) The default charset preference is set.
   1.420 +  //     (2) It's not a unicode charset.
   1.421 +  // For now, we use the language implied by the system locale as the
   1.422 +  // lang-group. The only exception is minimal font size, which is an
   1.423 +  // accessibility preference in Safari (under the Advanced tab). If it is set,
   1.424 +  // we set it for all languages.
   1.425 +  // As for the font type of the default font (serif/sans-serif), the default
   1.426 +  // type for the given language is used (set in font.default.LANGGROUP).
   1.427 +  _migrateFontSettings: function MPR__migrateFontSettings() {
   1.428 +    // If "Never use font sizes smaller than [ ] is set", migrate it for all
   1.429 +    // languages.
   1.430 +    if (this._dict.has("WebKitMinimumFontSize")) {
   1.431 +      let minimumSize = this._dict.get("WebKitMinimumFontSize");
   1.432 +      if (typeof(minimumSize) == "number") {
   1.433 +        let prefs = Services.prefs.getChildList("font.minimum-size");
   1.434 +        for (let pref of prefs) {
   1.435 +          Services.prefs.setIntPref(pref, minimumSize);
   1.436 +        }
   1.437 +      }
   1.438 +      else {
   1.439 +        Cu.reportError("WebKitMinimumFontSize was set to an invalid value: " +
   1.440 +                       minimumSize);
   1.441 +      }
   1.442 +    }
   1.443 +
   1.444 +    // In theory, the lang group could be "x-unicode". This will result
   1.445 +    // in setting the fonts for "Other Languages".
   1.446 +    let lang = this._getLocaleLangGroup();
   1.447 +
   1.448 +    let anySet = false;
   1.449 +    let fontType = Services.prefs.getCharPref("font.default." + lang);
   1.450 +    anySet |= this._set("WebKitFixedFont", "font.name.monospace." + lang);
   1.451 +    anySet |= this._set("WebKitDefaultFixedFontSize", "font.size.fixed." + lang);
   1.452 +    anySet |= this._set("WebKitStandardFont",
   1.453 +                        "font.name." + fontType + "." + lang);
   1.454 +    anySet |= this._set("WebKitDefaultFontSize", "font.size.variable." + lang);
   1.455 +
   1.456 +    // If we set font settings for a particular language, we'll also set the
   1.457 +    // fonts dialog to open with the fonts settings for that langauge.
   1.458 +    if (anySet)
   1.459 +      Services.prefs.setCharPref("font.language.group", lang);
   1.460 +  },
   1.461 +
   1.462 +  // Get the language group for the system locale.
   1.463 +  _getLocaleLangGroup: function MPR__getLocaleLangGroup() {
   1.464 +    let locale = Services.locale.getLocaleComponentForUserAgent();
   1.465 +
   1.466 +    // See nsLanguageAtomService::GetLanguageGroup
   1.467 +    let localeLangGroup = "x-unicode";
   1.468 +    let bundle = Services.strings.createBundle(
   1.469 +      "resource://gre/res/langGroups.properties");
   1.470 +    try {
   1.471 +      localeLangGroup = bundle.GetStringFromName(locale);
   1.472 +    }
   1.473 +    catch(ex) {
   1.474 +      let hyphenAt = locale.indexOf("-");
   1.475 +      if (hyphenAt != -1) {
   1.476 +        try {
   1.477 +          localeLangGroup = bundle.GetStringFromName(locale.substr(0, hyphenAt));
   1.478 +        }
   1.479 +        catch(ex2) { }
   1.480 +      }
   1.481 +    }
   1.482 +    return localeLangGroup;
   1.483 +  },
   1.484 +
   1.485 +  _migrateDownloadsFolder: function MPR__migrateDownloadsFolder() {
   1.486 +    // Windows Safari uses DownloadPath while Mac uses DownloadsPath.
   1.487 +    // Check both for future compatibility.
   1.488 +    let key;
   1.489 +    if (this._dict.has("DownloadsPath"))
   1.490 +      key = "DownloadsPath";
   1.491 +    else if (this._dict.has("DownloadPath"))
   1.492 +      key = "DownloadPath";
   1.493 +    else
   1.494 +      return;
   1.495 +
   1.496 +    let downloadsFolder = FileUtils.File(this._dict.get(key));
   1.497 +
   1.498 +    // If the download folder is set to the Desktop or to ~/Downloads, set the
   1.499 +    // folderList pref appropriately so that "Desktop"/Downloads is shown with
   1.500 +    // pretty name in the preferences dialog.
   1.501 +    let folderListVal = 2;
   1.502 +    if (downloadsFolder.equals(FileUtils.getDir("Desk", []))) {
   1.503 +      folderListVal = 0;
   1.504 +    }
   1.505 +    else {
   1.506 +      let dnldMgr = Cc["@mozilla.org/download-manager;1"].
   1.507 +                    getService(Ci.nsIDownloadManager);
   1.508 +      if (downloadsFolder.equals(dnldMgr.defaultDownloadsDirectory))
   1.509 +        folderListVal = 1;
   1.510 +    }
   1.511 +    Services.prefs.setIntPref("browser.download.folderList", folderListVal);
   1.512 +    Services.prefs.setComplexValue("browser.download.dir", Ci.nsILocalFile,
   1.513 +                                   downloadsFolder);
   1.514 +  }
   1.515 +};
   1.516 +
   1.517 +function SearchStrings(aMainPreferencesPropertyListInstance) {
   1.518 +  this._mainPreferencesPropertyList = aMainPreferencesPropertyListInstance;
   1.519 +}
   1.520 +SearchStrings.prototype = {
   1.521 +  type: MigrationUtils.resourceTypes.OTHERDATA,
   1.522 +
   1.523 +  migrate: function SS_migrate(aCallback) {
   1.524 +    this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
   1.525 +      function migrateSearchStrings(aDict) {
   1.526 +        if (!aDict)
   1.527 +          throw new Error("Could not get preferences dictionary");
   1.528 +
   1.529 +        if (aDict.has("RecentSearchStrings")) {
   1.530 +          let recentSearchStrings = aDict.get("RecentSearchStrings");
   1.531 +          if (recentSearchStrings && recentSearchStrings.length > 0) {
   1.532 +            let changes = [{op: "add",
   1.533 +                            fieldname: "searchbar-history",
   1.534 +                            value: searchString}
   1.535 +                           for (searchString of recentSearchStrings)];
   1.536 +            FormHistory.update(changes);
   1.537 +          }
   1.538 +        }
   1.539 +      }.bind(this), aCallback));
   1.540 +  }
   1.541 +};
   1.542 +
   1.543 +#ifdef XP_MACOSX
   1.544 +// On OS X, the cookie-accept policy preference is stored in a separate
   1.545 +// property list.
   1.546 +// For the Windows version, check Preferences.migrate.
   1.547 +function WebFoundationCookieBehavior(aWebFoundationFile) {
   1.548 +  this._file = aWebFoundationFile;
   1.549 +}
   1.550 +WebFoundationCookieBehavior.prototype = {
   1.551 +  type: MigrationUtils.resourceTypes.SETTINGS,
   1.552 +
   1.553 +  migrate: function WFPL_migrate(aCallback) {
   1.554 +    PropertyListUtils.read(this._file, MigrationUtils.wrapMigrateFunction(
   1.555 +      function migrateCookieBehavior(aDict) {
   1.556 +        if (!aDict)
   1.557 +          throw new Error("Could not read com.apple.WebFoundation.plist");
   1.558 +
   1.559 +        if (aDict.has("NSHTTPAcceptCookies")) {
   1.560 +          // Setting                    Safari          Firefox
   1.561 +          // Always Accept              always          0
   1.562 +          // Accept from Originating    current page    1
   1.563 +          // Never Accept               never           2
   1.564 +          let acceptCookies = aDict.get("NSHTTPAcceptCookies");
   1.565 +          let cookieValue = acceptCookies == "never" ? 2 :
   1.566 +                            acceptCookies == "current page" ? 1 : 0;
   1.567 +          Services.prefs.setIntPref("network.cookie.cookieBehavior",
   1.568 +                                    cookieValue);
   1.569 +        }
   1.570 +      }.bind(this), aCallback));
   1.571 +  }
   1.572 +};
   1.573 +#endif
   1.574 +
   1.575 +function SafariProfileMigrator() {
   1.576 +}
   1.577 +
   1.578 +SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
   1.579 +
   1.580 +SafariProfileMigrator.prototype.getResources = function SM_getResources() {
   1.581 +  let profileDir =
   1.582 +#ifdef XP_MACOSX
   1.583 +    FileUtils.getDir("ULibDir", ["Safari"], false);
   1.584 +#else
   1.585 +    FileUtils.getDir("AppData", ["Apple Computer", "Safari"], false);
   1.586 +#endif
   1.587 +  if (!profileDir.exists())
   1.588 +    return null;
   1.589 +
   1.590 +  let resources = [];
   1.591 +  let pushProfileFileResource = function(aFileName, aConstructor) {
   1.592 +    let file = profileDir.clone();
   1.593 +    file.append(aFileName);
   1.594 +    if (file.exists())
   1.595 +      resources.push(new aConstructor(file));
   1.596 +  };
   1.597 +
   1.598 +  pushProfileFileResource("History.plist", History);
   1.599 +  pushProfileFileResource("Bookmarks.plist", Bookmarks);
   1.600 +  
   1.601 +  // The Reading List feature was introduced at the same time in Windows and
   1.602 +  // Mac versions of Safari.  Not surprisingly, they are stored in the same
   1.603 +  // format in both versions.  Surpsingly, only on Windows there is a
   1.604 +  // separate property list for it.  This isn't #ifdefed out on mac, because
   1.605 +  // Apple may fix this at some point.
   1.606 +  pushProfileFileResource("ReadingList.plist", Bookmarks);
   1.607 +
   1.608 +  let prefsDir = 
   1.609 +#ifdef XP_MACOSX
   1.610 +    FileUtils.getDir("UsrPrfs", [], false);
   1.611 +#else
   1.612 +    FileUtils.getDir("AppData", ["Apple Computer", "Preferences"], false);
   1.613 +#endif
   1.614 +
   1.615 +  let prefs = this.mainPreferencesPropertyList;
   1.616 +  if (prefs) {
   1.617 +    resources.push(new Preferences(prefs));
   1.618 +    resources.push(new SearchStrings(prefs));
   1.619 +  }
   1.620 +
   1.621 +#ifdef XP_MACOSX
   1.622 +  // On OS X, the cookie-accept policy preference is stored in a separate
   1.623 +  // property list.
   1.624 +  let wfFile = FileUtils.getFile("UsrPrfs", ["com.apple.WebFoundation.plist"]);
   1.625 +  if (wfFile.exists())
   1.626 +    resources.push(new WebFoundationCookieBehavior(wfFile));
   1.627 +#endif
   1.628 +
   1.629 +  return resources;
   1.630 +};
   1.631 +
   1.632 +Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", {
   1.633 +  get: function get_mainPreferencesPropertyList() {
   1.634 +    if (this._mainPreferencesPropertyList === undefined) {
   1.635 +      let file = 
   1.636 +#ifdef XP_MACOSX
   1.637 +        FileUtils.getDir("UsrPrfs", [], false);
   1.638 +#else
   1.639 +        FileUtils.getDir("AppData", ["Apple Computer", "Preferences"], false);
   1.640 +#endif
   1.641 +      if (file.exists()) {
   1.642 +        file.append("com.apple.Safari.plist");
   1.643 +        if (file.exists()) {
   1.644 +          return this._mainPreferencesPropertyList =
   1.645 +            new MainPreferencesPropertyList(file);
   1.646 +        }
   1.647 +      }
   1.648 +      return this._mainPreferencesPropertyList = null;
   1.649 +    }
   1.650 +    return this._mainPreferencesPropertyList;
   1.651 +  }
   1.652 +});
   1.653 +
   1.654 +Object.defineProperty(SafariProfileMigrator.prototype, "sourceHomePageURL", {
   1.655 +  get: function get_sourceHomePageURL() {
   1.656 +    if (this.mainPreferencesPropertyList) {
   1.657 +      let dict = this.mainPreferencesPropertyList._readSync();
   1.658 +      if (dict.has("HomePage"))
   1.659 +        return dict.get("HomePage");
   1.660 +    }
   1.661 +    return "";
   1.662 +  }
   1.663 +});
   1.664 +
   1.665 +SafariProfileMigrator.prototype.classDescription = "Safari Profile Migrator";
   1.666 +SafariProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=safari";
   1.667 +SafariProfileMigrator.prototype.classID = Components.ID("{4b609ecf-60b2-4655-9df4-dc149e474da1}");
   1.668 +
   1.669 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SafariProfileMigrator]);

mercurial