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]);