browser/components/migration/src/IEProfileMigrator.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/components/migration/src/IEProfileMigrator.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,645 @@
     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 file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +const Cc = Components.classes;
    1.11 +const Ci = Components.interfaces;
    1.12 +const Cu = Components.utils;
    1.13 +const Cr = Components.results;
    1.14 +
    1.15 +const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
    1.16 +
    1.17 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.18 +Cu.import("resource://gre/modules/Services.jsm");
    1.19 +Cu.import("resource://gre/modules/NetUtil.jsm");
    1.20 +Cu.import("resource:///modules/MigrationUtils.jsm");
    1.21 +
    1.22 +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
    1.23 +                                  "resource://gre/modules/PlacesUtils.jsm");
    1.24 +XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
    1.25 +                                  "resource://gre/modules/ctypes.jsm");
    1.26 +XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
    1.27 +                                  "resource://gre/modules/WindowsRegistry.jsm");
    1.28 +
    1.29 +////////////////////////////////////////////////////////////////////////////////
    1.30 +//// Helpers.
    1.31 +
    1.32 +let CtypesHelpers = {
    1.33 +  _structs: {},
    1.34 +  _functions: {},
    1.35 +  _libs: {},
    1.36 +
    1.37 +  /**
    1.38 +   * Must be invoked once before first use of any of the provided helpers.
    1.39 +   */
    1.40 +  initialize: function CH_initialize() {
    1.41 +    const WORD = ctypes.uint16_t;
    1.42 +    const DWORD = ctypes.uint32_t;
    1.43 +    const BOOL = ctypes.int;
    1.44 +
    1.45 +    this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
    1.46 +      {wYear: WORD},
    1.47 +      {wMonth: WORD},
    1.48 +      {wDayOfWeek: WORD},
    1.49 +      {wDay: WORD},
    1.50 +      {wHour: WORD},
    1.51 +      {wMinute: WORD},
    1.52 +      {wSecond: WORD},
    1.53 +      {wMilliseconds: WORD}
    1.54 +    ]);
    1.55 +
    1.56 +    this._structs.FILETIME = new ctypes.StructType('FILETIME', [
    1.57 +      {dwLowDateTime: DWORD},
    1.58 +      {dwHighDateTime: DWORD}
    1.59 +    ]);
    1.60 +
    1.61 +    try {
    1.62 +      this._libs.kernel32 = ctypes.open("Kernel32");
    1.63 +      this._functions.FileTimeToSystemTime =
    1.64 +        this._libs.kernel32.declare("FileTimeToSystemTime",
    1.65 +                                    ctypes.default_abi,
    1.66 +                                    BOOL,
    1.67 +                                    this._structs.FILETIME.ptr,
    1.68 +                                    this._structs.SYSTEMTIME.ptr);
    1.69 +    } catch (ex) {
    1.70 +      this.finalize();
    1.71 +    }
    1.72 +  },
    1.73 +
    1.74 +  /**
    1.75 +   * Must be invoked once after last use of any of the provided helpers.
    1.76 +   */
    1.77 +  finalize: function CH_finalize() {
    1.78 +    this._structs = {};
    1.79 +    this._functions = {};
    1.80 +    for each (let lib in this._libs) {
    1.81 +      try {
    1.82 +        lib.close();
    1.83 +      } catch (ex) {}
    1.84 +    }
    1.85 +    this._libs = {};
    1.86 +  },
    1.87 +
    1.88 +  /**
    1.89 +   * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct.
    1.90 +   *
    1.91 +   * @param aTimeHi
    1.92 +   *        Least significant DWORD.
    1.93 +   * @param aTimeLo
    1.94 +   *        Most significant DWORD.
    1.95 +   * @return a Date object representing the converted datetime.
    1.96 +   */
    1.97 +  fileTimeToDate: function CH_fileTimeToDate(aTimeHi, aTimeLo) {
    1.98 +    let fileTime = this._structs.FILETIME();
    1.99 +    fileTime.dwLowDateTime = aTimeLo;
   1.100 +    fileTime.dwHighDateTime = aTimeHi;
   1.101 +    let systemTime = this._structs.SYSTEMTIME();
   1.102 +    let result = this._functions.FileTimeToSystemTime(fileTime.address(),
   1.103 +                                                      systemTime.address());
   1.104 +    if (result == 0)
   1.105 +      throw new Error(ctypes.winLastError);
   1.106 +
   1.107 +    return new Date(systemTime.wYear,
   1.108 +                    systemTime.wMonth - 1,
   1.109 +                    systemTime.wDay,
   1.110 +                    systemTime.wHour,
   1.111 +                    systemTime.wMinute,
   1.112 +                    systemTime.wSecond,
   1.113 +                    systemTime.wMilliseconds);
   1.114 +  }
   1.115 +};
   1.116 +
   1.117 +/**
   1.118 + * Checks whether an host is an IP (v4 or v6) address.
   1.119 + *
   1.120 + * @param aHost
   1.121 + *        The host to check.
   1.122 + * @return whether aHost is an IP address.
   1.123 + */
   1.124 +function hostIsIPAddress(aHost) {
   1.125 +  try {
   1.126 +    Services.eTLD.getBaseDomainFromHost(aHost);
   1.127 +  } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
   1.128 +    return true;
   1.129 +  } catch (e) {}
   1.130 +  return false;
   1.131 +}
   1.132 +
   1.133 +////////////////////////////////////////////////////////////////////////////////
   1.134 +//// Resources
   1.135 +
   1.136 +function Bookmarks() {
   1.137 +}
   1.138 +
   1.139 +Bookmarks.prototype = {
   1.140 +  type: MigrationUtils.resourceTypes.BOOKMARKS,
   1.141 +
   1.142 +  get exists() !!this._favoritesFolder,
   1.143 +
   1.144 +  __favoritesFolder: null,
   1.145 +  get _favoritesFolder() {
   1.146 +    if (!this.__favoritesFolder) {
   1.147 +      let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile);
   1.148 +      if (favoritesFolder.exists() && favoritesFolder.isReadable())
   1.149 +        this.__favoritesFolder = favoritesFolder;
   1.150 +    }
   1.151 +    return this.__favoritesFolder;
   1.152 +  },
   1.153 +
   1.154 +  __toolbarFolderName: null,
   1.155 +  get _toolbarFolderName() {
   1.156 +    if (!this.__toolbarFolderName) {
   1.157 +      // Retrieve the name of IE's favorites subfolder that holds the bookmarks
   1.158 +      // in the toolbar. This was previously stored in the registry and changed
   1.159 +      // in IE7 to always be called "Links".
   1.160 +      let folderName = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
   1.161 +                                                  "Software\\Microsoft\\Internet Explorer\\Toolbar",
   1.162 +                                                  "LinksFolderName");
   1.163 +      this.__toolbarFolderName = folderName || "Links";
   1.164 +    }
   1.165 +    return this.__toolbarFolderName;
   1.166 +  },
   1.167 +
   1.168 +  migrate: function B_migrate(aCallback) {
   1.169 +    PlacesUtils.bookmarks.runInBatchMode({
   1.170 +      runBatched: (function migrateBatched() {
   1.171 +        // Import to the bookmarks menu.
   1.172 +        let destFolderId = PlacesUtils.bookmarksMenuFolderId;
   1.173 +        if (!MigrationUtils.isStartupMigration) {
   1.174 +          destFolderId =
   1.175 +            MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
   1.176 +        }
   1.177 +
   1.178 +        this._migrateFolder(this._favoritesFolder, destFolderId);
   1.179 +
   1.180 +        aCallback(true);
   1.181 +      }).bind(this)
   1.182 +    }, null);
   1.183 +  },
   1.184 +
   1.185 +  _migrateFolder: function B__migrateFolder(aSourceFolder, aDestFolderId) {
   1.186 +    // TODO (bug 741993): the favorites order is stored in the Registry, at
   1.187 +    // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
   1.188 +    // Until we support it, bookmarks are imported in alphabetical order.
   1.189 +    let entries = aSourceFolder.directoryEntries;
   1.190 +    while (entries.hasMoreElements()) {
   1.191 +      let entry = entries.getNext().QueryInterface(Ci.nsIFile);
   1.192 +      try {
   1.193 +        // Make sure that entry.path == entry.target to not follow .lnk folder
   1.194 +        // shortcuts which could lead to infinite cycles.
   1.195 +        // Don't use isSymlink(), since it would throw for invalid
   1.196 +        // lnk files pointing to URLs or to unresolvable paths.
   1.197 +        if (entry.path == entry.target && entry.isDirectory()) {
   1.198 +          let destFolderId;
   1.199 +          if (entry.leafName == this._toolbarFolderName &&
   1.200 +              entry.parent.equals(this._favoritesFolder)) {
   1.201 +            // Import to the bookmarks toolbar.
   1.202 +            destFolderId = PlacesUtils.toolbarFolderId;
   1.203 +            if (!MigrationUtils.isStartupMigration) {
   1.204 +              destFolderId =
   1.205 +                MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
   1.206 +            }
   1.207 +          }
   1.208 +          else {
   1.209 +            // Import to a new folder.
   1.210 +            destFolderId =
   1.211 +              PlacesUtils.bookmarks.createFolder(aDestFolderId, entry.leafName,
   1.212 +                                                 PlacesUtils.bookmarks.DEFAULT_INDEX);
   1.213 +          }
   1.214 +
   1.215 +          if (entry.isReadable()) {
   1.216 +            // Recursively import the folder.
   1.217 +            this._migrateFolder(entry, destFolderId);
   1.218 +          }
   1.219 +        }
   1.220 +        else {
   1.221 +          // Strip the .url extension, to both check this is a valid link file,
   1.222 +          // and get the associated title.
   1.223 +          let matches = entry.leafName.match(/(.+)\.url$/i);
   1.224 +          if (matches) {
   1.225 +            let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
   1.226 +                              getService(Ci.nsIFileProtocolHandler);
   1.227 +            let uri = fileHandler.readURLFile(entry);
   1.228 +            let title = matches[1];
   1.229 +
   1.230 +            PlacesUtils.bookmarks.insertBookmark(aDestFolderId,
   1.231 +                                                 uri,
   1.232 +                                                 PlacesUtils.bookmarks.DEFAULT_INDEX,
   1.233 +                                                 title);
   1.234 +          }
   1.235 +        }
   1.236 +      } catch (ex) {
   1.237 +        Components.utils.reportError("Unable to import IE favorite (" + entry.leafName + "): " + ex);
   1.238 +      }
   1.239 +    }
   1.240 +  }
   1.241 +};
   1.242 +
   1.243 +function History() {
   1.244 +}
   1.245 +
   1.246 +History.prototype = {
   1.247 +  type: MigrationUtils.resourceTypes.HISTORY,
   1.248 +
   1.249 +  get exists() true,
   1.250 +
   1.251 +  __typedURLs: null,
   1.252 +  get _typedURLs() {
   1.253 +    if (!this.__typedURLs) {
   1.254 +      // The list of typed URLs is a sort of annotation stored in the registry.
   1.255 +      // Currently, IE stores 25 entries and this value is not configurable,
   1.256 +      // but we just keep reading up to the first non-existing entry to support
   1.257 +      // possible future bumps of this limit.
   1.258 +      this.__typedURLs = {};
   1.259 +      let registry = Cc["@mozilla.org/windows-registry-key;1"].
   1.260 +                     createInstance(Ci.nsIWindowsRegKey);
   1.261 +      try {
   1.262 +        registry.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
   1.263 +                      "Software\\Microsoft\\Internet Explorer\\TypedURLs",
   1.264 +                      Ci.nsIWindowsRegKey.ACCESS_READ);
   1.265 +        for (let entry = 1; registry.hasValue("url" + entry); entry++) {
   1.266 +          let url = registry.readStringValue("url" + entry);
   1.267 +          this.__typedURLs[url] = true;
   1.268 +        }
   1.269 +      } catch (ex) {
   1.270 +      } finally {
   1.271 +        registry.close();
   1.272 +      }
   1.273 +    }
   1.274 +    return this.__typedURLs;
   1.275 +  },
   1.276 +
   1.277 +  migrate: function H_migrate(aCallback) {
   1.278 +    let places = [];
   1.279 +    let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
   1.280 +                            createInstance(Ci.nsISimpleEnumerator);
   1.281 +    while (historyEnumerator.hasMoreElements()) {
   1.282 +      let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
   1.283 +      let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
   1.284 +      // MSIE stores some types of URLs in its history that we don't handle,
   1.285 +      // like HTMLHelp and others.  Since we don't properly map handling for
   1.286 +      // all of them we just avoid importing them.
   1.287 +      if (["http", "https", "ftp", "file"].indexOf(uri.scheme) == -1) {
   1.288 +        continue;
   1.289 +      }
   1.290 +
   1.291 +      let title = entry.get("title");
   1.292 +      // Embed visits have no title and don't need to be imported.
   1.293 +      if (title.length == 0) {
   1.294 +        continue;
   1.295 +      }
   1.296 +
   1.297 +      // The typed urls are already fixed-up, so we can use them for comparison.
   1.298 +      let transitionType = this._typedURLs[uri.spec] ?
   1.299 +                             Ci.nsINavHistoryService.TRANSITION_TYPED :
   1.300 +                             Ci.nsINavHistoryService.TRANSITION_LINK;
   1.301 +      let lastVisitTime = entry.get("time");
   1.302 +
   1.303 +      places.push(
   1.304 +        { uri: uri,
   1.305 +          title: title,
   1.306 +          visits: [{ transitionType: transitionType,
   1.307 +                     visitDate: lastVisitTime }]
   1.308 +        }
   1.309 +      );
   1.310 +    }
   1.311 +
   1.312 +    // Check whether there is any history to import.
   1.313 +    if (places.length == 0) {
   1.314 +      aCallback(true);
   1.315 +      return;
   1.316 +    }
   1.317 +
   1.318 +    PlacesUtils.asyncHistory.updatePlaces(places, {
   1.319 +      _success: false,
   1.320 +      handleResult: function() {
   1.321 +        // Importing any entry is considered a successful import.
   1.322 +        this._success = true;
   1.323 +      },
   1.324 +      handleError: function() {},
   1.325 +      handleCompletion: function() {
   1.326 +        aCallback(this._success);
   1.327 +      }
   1.328 +    });
   1.329 +  }
   1.330 +};
   1.331 +
   1.332 +function Cookies() {
   1.333 +}
   1.334 +
   1.335 +Cookies.prototype = {
   1.336 +  type: MigrationUtils.resourceTypes.COOKIES,
   1.337 +
   1.338 +  get exists() !!this._cookiesFolder,
   1.339 +
   1.340 +  __cookiesFolder: null,
   1.341 +  get _cookiesFolder() {
   1.342 +    // Cookies are stored in txt files, in a Cookies folder whose path varies
   1.343 +    // across the different OS versions.  CookD takes care of most of these
   1.344 +    // cases, though, in Windows Vista/7, UAC makes a difference.
   1.345 +    // If UAC is enabled, the most common destination is CookD/Low.  Though,
   1.346 +    // if the user runs the application in administrator mode or disables UAC,
   1.347 +    // cookies are stored in the original CookD destination.  Cause running the
   1.348 +    // browser in administrator mode is unsafe and discouraged, we just care
   1.349 +    // about the UAC state.
   1.350 +    if (!this.__cookiesFolder) {
   1.351 +      let cookiesFolder = Services.dirsvc.get("CookD", Ci.nsIFile);
   1.352 +      if (cookiesFolder.exists() && cookiesFolder.isReadable()) {
   1.353 +        // Check if UAC is enabled.
   1.354 +        if (Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).userCanElevate) {
   1.355 +          cookiesFolder.append("Low");
   1.356 +        }
   1.357 +        this.__cookiesFolder = cookiesFolder;
   1.358 +      }
   1.359 +    }
   1.360 +    return this.__cookiesFolder;
   1.361 +  },
   1.362 +
   1.363 +  migrate: function C_migrate(aCallback) {
   1.364 +    CtypesHelpers.initialize();
   1.365 +
   1.366 +    let cookiesGenerator = (function genCookie() {
   1.367 +      let success = false;
   1.368 +      let entries = this._cookiesFolder.directoryEntries;
   1.369 +      while (entries.hasMoreElements()) {
   1.370 +        let entry = entries.getNext().QueryInterface(Ci.nsIFile);
   1.371 +        // Skip eventual bogus entries.
   1.372 +        if (!entry.isFile() || !/\.txt$/.test(entry.leafName))
   1.373 +          continue;
   1.374 +
   1.375 +        this._readCookieFile(entry, function(aSuccess) {
   1.376 +          // Importing even a single cookie file is considered a success.
   1.377 +          if (aSuccess)
   1.378 +            success = true;
   1.379 +          try {
   1.380 +            cookiesGenerator.next();
   1.381 +          } catch (ex) {}
   1.382 +        });
   1.383 +
   1.384 +        yield undefined;
   1.385 +      }
   1.386 +
   1.387 +      CtypesHelpers.finalize();
   1.388 +
   1.389 +      aCallback(success);
   1.390 +    }).apply(this);
   1.391 +    cookiesGenerator.next();
   1.392 +  },
   1.393 +
   1.394 +  _readCookieFile: function C__readCookieFile(aFile, aCallback) {
   1.395 +    let fileReader = Cc["@mozilla.org/files/filereader;1"].
   1.396 +                     createInstance(Ci.nsIDOMFileReader);
   1.397 +    fileReader.addEventListener("loadend", (function onLoadEnd() {
   1.398 +      fileReader.removeEventListener("loadend", onLoadEnd, false);
   1.399 +
   1.400 +      if (fileReader.readyState != fileReader.DONE) {
   1.401 +        Cu.reportError("Could not read cookie contents: " + fileReader.error);
   1.402 +        aCallback(false);
   1.403 +        return;
   1.404 +      }
   1.405 +
   1.406 +      let success = true;
   1.407 +      try {
   1.408 +        this._parseCookieBuffer(fileReader.result);
   1.409 +      } catch (ex) {
   1.410 +        Components.utils.reportError("Unable to migrate cookie: " + ex);
   1.411 +        success = false;
   1.412 +      } finally {
   1.413 +        aCallback(success);
   1.414 +      }
   1.415 +    }).bind(this), false);
   1.416 +    fileReader.readAsText(File(aFile));
   1.417 +  },
   1.418 +
   1.419 +  /**
   1.420 +   * Parses a cookie file buffer and returns an array of the contained cookies.
   1.421 +   *
   1.422 +   * The cookie file format is a newline-separated-values with a "*" used as
   1.423 +   * delimeter between multiple records.
   1.424 +   * Each cookie has the following fields:
   1.425 +   *  - name
   1.426 +   *  - value
   1.427 +   *  - host/path
   1.428 +   *  - flags
   1.429 +   *  - Expiration time most significant integer
   1.430 +   *  - Expiration time least significant integer
   1.431 +   *  - Creation time most significant integer
   1.432 +   *  - Creation time least significant integer
   1.433 +   *  - Record delimiter "*"
   1.434 +   *
   1.435 +   * @note All the times are in FILETIME format.
   1.436 +   */
   1.437 +  _parseCookieBuffer: function C__parseCookieBuffer(aTextBuffer) {
   1.438 +    // Note the last record is an empty string.
   1.439 +    let records = [r for each (r in aTextBuffer.split("*\n")) if (r)];
   1.440 +    for (let record of records) {
   1.441 +      let [name, value, hostpath, flags,
   1.442 +           expireTimeLo, expireTimeHi] = record.split("\n");
   1.443 +
   1.444 +      // IE stores deleted cookies with a zero-length value, skip them.
   1.445 +      if (value.length == 0)
   1.446 +        continue;
   1.447 +
   1.448 +      let hostLen = hostpath.indexOf("/");
   1.449 +      let host = hostpath.substr(0, hostLen);
   1.450 +
   1.451 +      // For a non-null domain, assume it's what Mozilla considers
   1.452 +      // a domain cookie.  See bug 222343.
   1.453 +      if (host.length > 0) {
   1.454 +        // Fist delete any possible extant matching host cookie.
   1.455 +        Services.cookies.remove(host, name, path, false);
   1.456 +        // Now make it a domain cookie.
   1.457 +        if (host[0] != "." && !hostIsIPAddress(host))
   1.458 +          host = "." + host;
   1.459 +      }
   1.460 +
   1.461 +      let path = hostpath.substr(hostLen);
   1.462 +      let expireTime = CtypesHelpers.fileTimeToDate(Number(expireTimeHi),
   1.463 +                                                    Number(expireTimeLo));
   1.464 +      Services.cookies.add(host,
   1.465 +                           path,
   1.466 +                           name,
   1.467 +                           value,
   1.468 +                           Number(flags) & 0x1, // secure
   1.469 +                           false, // httpOnly
   1.470 +                           false, // session
   1.471 +                           expireTime);
   1.472 +    }
   1.473 +  }
   1.474 +};
   1.475 +
   1.476 +function Settings() {
   1.477 +}
   1.478 +
   1.479 +Settings.prototype = {
   1.480 +  type: MigrationUtils.resourceTypes.SETTINGS,
   1.481 +
   1.482 +  get exists() true,
   1.483 +
   1.484 +  migrate: function S_migrate(aCallback) {
   1.485 +    // Converts from yes/no to a boolean.
   1.486 +    function yesNoToBoolean(v) v == "yes";
   1.487 +
   1.488 +    // Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
   1.489 +    // destination format like "en-us, ar-kw, ar-om".
   1.490 +    // Final string is sorted by quality (q=) param.
   1.491 +    function parseAcceptLanguageList(v) {
   1.492 +      return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
   1.493 +              .sort(function (a , b) {
   1.494 +                let qA = parseFloat(a.split(";q=")[1]) || 1.0;
   1.495 +                let qB = parseFloat(b.split(";q=")[1]) || 1.0;
   1.496 +                return qA < qB ? 1 : qA == qB ? 0 : -1;
   1.497 +              })
   1.498 +              .map(function(a) a.split(";")[0]);
   1.499 +    }
   1.500 +
   1.501 +    // For reference on some of the available IE Registry settings:
   1.502 +    //  * http://msdn.microsoft.com/en-us/library/cc980058%28v=prot.13%29.aspx
   1.503 +    //  * http://msdn.microsoft.com/en-us/library/cc980059%28v=prot.13%29.aspx
   1.504 +
   1.505 +    // Note that only settings exposed in our UI should be migrated.
   1.506 +
   1.507 +    this._set("Software\\Microsoft\\Internet Explorer\\International",
   1.508 +              "AcceptLanguage",
   1.509 +              "intl.accept_languages",
   1.510 +              parseAcceptLanguageList);
   1.511 +    // TODO (bug 745853): For now, only x-western font is translated.
   1.512 +    this._set("Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
   1.513 +              "IEFixedFontName",
   1.514 +              "font.name.monospace.x-western");
   1.515 +    this._set(kMainKey,
   1.516 +              "Use FormSuggest",
   1.517 +              "browser.formfill.enable",
   1.518 +              yesNoToBoolean);
   1.519 +    this._set(kMainKey,
   1.520 +              "FormSuggest Passwords",
   1.521 +              "signon.rememberSignons",
   1.522 +              yesNoToBoolean);
   1.523 +    this._set(kMainKey,
   1.524 +              "Anchor Underline",
   1.525 +              "browser.underline_anchors",
   1.526 +              yesNoToBoolean);
   1.527 +    this._set(kMainKey,
   1.528 +              "Display Inline Images",
   1.529 +              "permissions.default.image",
   1.530 +              function (v) yesNoToBoolean(v) ? 1 : 2);
   1.531 +    this._set(kMainKey,
   1.532 +              "Move System Caret",
   1.533 +              "accessibility.browsewithcaret",
   1.534 +              yesNoToBoolean);
   1.535 +    this._set("Software\\Microsoft\\Internet Explorer\\Settings",
   1.536 +              "Always Use My Colors",
   1.537 +              "browser.display.use_document_colors",
   1.538 +              function (v) !Boolean(v));
   1.539 +    this._set("Software\\Microsoft\\Internet Explorer\\Settings",
   1.540 +              "Always Use My Font Face",
   1.541 +              "browser.display.use_document_fonts",
   1.542 +              function (v) !Boolean(v));
   1.543 +    this._set(kMainKey,
   1.544 +              "SmoothScroll",
   1.545 +              "general.smoothScroll",
   1.546 +              Boolean);
   1.547 +    this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
   1.548 +              "WarnOnClose",
   1.549 +              "browser.tabs.warnOnClose",
   1.550 +              Boolean);
   1.551 +    this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
   1.552 +              "OpenInForeground",
   1.553 +              "browser.tabs.loadInBackground",
   1.554 +              function (v) !Boolean(v));
   1.555 +
   1.556 +    aCallback(true);
   1.557 +  },
   1.558 +
   1.559 +  /**
   1.560 +   * Reads a setting from the Registry and stores the converted result into
   1.561 +   * the appropriate Firefox preference.
   1.562 +   * 
   1.563 +   * @param aPath
   1.564 +   *        Registry path under HKCU.
   1.565 +   * @param aKey
   1.566 +   *        Name of the key.
   1.567 +   * @param aPref
   1.568 +   *        Firefox preference.
   1.569 +   * @param [optional] aTransformFn
   1.570 +   *        Conversion function from the Registry format to the pref format.
   1.571 +   */
   1.572 +  _set: function S__set(aPath, aKey, aPref, aTransformFn) {
   1.573 +    let value = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
   1.574 +                                           aPath, aKey);
   1.575 +    // Don't import settings that have never been flipped.
   1.576 +    if (value === undefined)
   1.577 +      return;
   1.578 +
   1.579 +    if (aTransformFn)
   1.580 +      value = aTransformFn(value);
   1.581 +
   1.582 +    switch (typeof(value)) {
   1.583 +      case "string":
   1.584 +        Services.prefs.setCharPref(aPref, value);
   1.585 +        break;
   1.586 +      case "number":
   1.587 +        Services.prefs.setIntPref(aPref, value);
   1.588 +        break;
   1.589 +      case "boolean":
   1.590 +        Services.prefs.setBoolPref(aPref, value);
   1.591 +        break;
   1.592 +      default:
   1.593 +        throw new Error("Unexpected value type: " + typeof(value));
   1.594 +    }
   1.595 +  }
   1.596 +};
   1.597 +
   1.598 +////////////////////////////////////////////////////////////////////////////////
   1.599 +//// Migrator
   1.600 +
   1.601 +function IEProfileMigrator()
   1.602 +{
   1.603 +}
   1.604 +
   1.605 +IEProfileMigrator.prototype = Object.create(MigratorPrototype);
   1.606 +
   1.607 +IEProfileMigrator.prototype.getResources = function IE_getResources() {
   1.608 +  let resources = [
   1.609 +    new Bookmarks()
   1.610 +  , new History()
   1.611 +  , new Cookies()
   1.612 +  , new Settings()
   1.613 +  ];
   1.614 +  return [r for each (r in resources) if (r.exists)];
   1.615 +};
   1.616 +
   1.617 +Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
   1.618 +  get: function IE_get_sourceHomePageURL() {
   1.619 +    let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
   1.620 +                                                      kMainKey, "Default_Page_URL");
   1.621 +    let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
   1.622 +                                               kMainKey, "Start Page");
   1.623 +    // If the user didn't customize the Start Page, he is still on the default
   1.624 +    // page, that may be considered the equivalent of our about:home.  There's
   1.625 +    // no reason to retain it, since it is heavily targeted to IE.
   1.626 +    let homepage = startPage != defaultStartPage ? startPage : "";
   1.627 +
   1.628 +    // IE7+ supports secondary home pages located in a REG_MULTI_SZ key.  These
   1.629 +    // are in addition to the Start Page, and no empty entries are possible,
   1.630 +    // thus a Start Page is always defined if any of these exists, though it
   1.631 +    // may be the default one.
   1.632 +    let secondaryPages = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
   1.633 +                                                    kMainKey, "Secondary Start Pages");
   1.634 +    if (secondaryPages) {
   1.635 +      if (homepage)
   1.636 +        secondaryPages.unshift(homepage);
   1.637 +      homepage = secondaryPages.join("|");
   1.638 +    }
   1.639 +
   1.640 +    return homepage;
   1.641 +  }
   1.642 +});
   1.643 +
   1.644 +IEProfileMigrator.prototype.classDescription = "IE Profile Migrator";
   1.645 +IEProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=ie";
   1.646 +IEProfileMigrator.prototype.classID = Components.ID("{3d2532e3-4932-4774-b7ba-968f5899d3a4}");
   1.647 +
   1.648 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IEProfileMigrator]);

mercurial