Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const Cc = Components.classes; |
michael@0 | 8 | const Ci = Components.interfaces; |
michael@0 | 9 | const Cu = Components.utils; |
michael@0 | 10 | const Cr = Components.results; |
michael@0 | 11 | |
michael@0 | 12 | const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main"; |
michael@0 | 13 | |
michael@0 | 14 | Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
michael@0 | 15 | Cu.import("resource://gre/modules/Services.jsm"); |
michael@0 | 16 | Cu.import("resource://gre/modules/NetUtil.jsm"); |
michael@0 | 17 | Cu.import("resource:///modules/MigrationUtils.jsm"); |
michael@0 | 18 | |
michael@0 | 19 | XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", |
michael@0 | 20 | "resource://gre/modules/PlacesUtils.jsm"); |
michael@0 | 21 | XPCOMUtils.defineLazyModuleGetter(this, "ctypes", |
michael@0 | 22 | "resource://gre/modules/ctypes.jsm"); |
michael@0 | 23 | XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry", |
michael@0 | 24 | "resource://gre/modules/WindowsRegistry.jsm"); |
michael@0 | 25 | |
michael@0 | 26 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 27 | //// Helpers. |
michael@0 | 28 | |
michael@0 | 29 | let CtypesHelpers = { |
michael@0 | 30 | _structs: {}, |
michael@0 | 31 | _functions: {}, |
michael@0 | 32 | _libs: {}, |
michael@0 | 33 | |
michael@0 | 34 | /** |
michael@0 | 35 | * Must be invoked once before first use of any of the provided helpers. |
michael@0 | 36 | */ |
michael@0 | 37 | initialize: function CH_initialize() { |
michael@0 | 38 | const WORD = ctypes.uint16_t; |
michael@0 | 39 | const DWORD = ctypes.uint32_t; |
michael@0 | 40 | const BOOL = ctypes.int; |
michael@0 | 41 | |
michael@0 | 42 | this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [ |
michael@0 | 43 | {wYear: WORD}, |
michael@0 | 44 | {wMonth: WORD}, |
michael@0 | 45 | {wDayOfWeek: WORD}, |
michael@0 | 46 | {wDay: WORD}, |
michael@0 | 47 | {wHour: WORD}, |
michael@0 | 48 | {wMinute: WORD}, |
michael@0 | 49 | {wSecond: WORD}, |
michael@0 | 50 | {wMilliseconds: WORD} |
michael@0 | 51 | ]); |
michael@0 | 52 | |
michael@0 | 53 | this._structs.FILETIME = new ctypes.StructType('FILETIME', [ |
michael@0 | 54 | {dwLowDateTime: DWORD}, |
michael@0 | 55 | {dwHighDateTime: DWORD} |
michael@0 | 56 | ]); |
michael@0 | 57 | |
michael@0 | 58 | try { |
michael@0 | 59 | this._libs.kernel32 = ctypes.open("Kernel32"); |
michael@0 | 60 | this._functions.FileTimeToSystemTime = |
michael@0 | 61 | this._libs.kernel32.declare("FileTimeToSystemTime", |
michael@0 | 62 | ctypes.default_abi, |
michael@0 | 63 | BOOL, |
michael@0 | 64 | this._structs.FILETIME.ptr, |
michael@0 | 65 | this._structs.SYSTEMTIME.ptr); |
michael@0 | 66 | } catch (ex) { |
michael@0 | 67 | this.finalize(); |
michael@0 | 68 | } |
michael@0 | 69 | }, |
michael@0 | 70 | |
michael@0 | 71 | /** |
michael@0 | 72 | * Must be invoked once after last use of any of the provided helpers. |
michael@0 | 73 | */ |
michael@0 | 74 | finalize: function CH_finalize() { |
michael@0 | 75 | this._structs = {}; |
michael@0 | 76 | this._functions = {}; |
michael@0 | 77 | for each (let lib in this._libs) { |
michael@0 | 78 | try { |
michael@0 | 79 | lib.close(); |
michael@0 | 80 | } catch (ex) {} |
michael@0 | 81 | } |
michael@0 | 82 | this._libs = {}; |
michael@0 | 83 | }, |
michael@0 | 84 | |
michael@0 | 85 | /** |
michael@0 | 86 | * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct. |
michael@0 | 87 | * |
michael@0 | 88 | * @param aTimeHi |
michael@0 | 89 | * Least significant DWORD. |
michael@0 | 90 | * @param aTimeLo |
michael@0 | 91 | * Most significant DWORD. |
michael@0 | 92 | * @return a Date object representing the converted datetime. |
michael@0 | 93 | */ |
michael@0 | 94 | fileTimeToDate: function CH_fileTimeToDate(aTimeHi, aTimeLo) { |
michael@0 | 95 | let fileTime = this._structs.FILETIME(); |
michael@0 | 96 | fileTime.dwLowDateTime = aTimeLo; |
michael@0 | 97 | fileTime.dwHighDateTime = aTimeHi; |
michael@0 | 98 | let systemTime = this._structs.SYSTEMTIME(); |
michael@0 | 99 | let result = this._functions.FileTimeToSystemTime(fileTime.address(), |
michael@0 | 100 | systemTime.address()); |
michael@0 | 101 | if (result == 0) |
michael@0 | 102 | throw new Error(ctypes.winLastError); |
michael@0 | 103 | |
michael@0 | 104 | return new Date(systemTime.wYear, |
michael@0 | 105 | systemTime.wMonth - 1, |
michael@0 | 106 | systemTime.wDay, |
michael@0 | 107 | systemTime.wHour, |
michael@0 | 108 | systemTime.wMinute, |
michael@0 | 109 | systemTime.wSecond, |
michael@0 | 110 | systemTime.wMilliseconds); |
michael@0 | 111 | } |
michael@0 | 112 | }; |
michael@0 | 113 | |
michael@0 | 114 | /** |
michael@0 | 115 | * Checks whether an host is an IP (v4 or v6) address. |
michael@0 | 116 | * |
michael@0 | 117 | * @param aHost |
michael@0 | 118 | * The host to check. |
michael@0 | 119 | * @return whether aHost is an IP address. |
michael@0 | 120 | */ |
michael@0 | 121 | function hostIsIPAddress(aHost) { |
michael@0 | 122 | try { |
michael@0 | 123 | Services.eTLD.getBaseDomainFromHost(aHost); |
michael@0 | 124 | } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) { |
michael@0 | 125 | return true; |
michael@0 | 126 | } catch (e) {} |
michael@0 | 127 | return false; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 131 | //// Resources |
michael@0 | 132 | |
michael@0 | 133 | function Bookmarks() { |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | Bookmarks.prototype = { |
michael@0 | 137 | type: MigrationUtils.resourceTypes.BOOKMARKS, |
michael@0 | 138 | |
michael@0 | 139 | get exists() !!this._favoritesFolder, |
michael@0 | 140 | |
michael@0 | 141 | __favoritesFolder: null, |
michael@0 | 142 | get _favoritesFolder() { |
michael@0 | 143 | if (!this.__favoritesFolder) { |
michael@0 | 144 | let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile); |
michael@0 | 145 | if (favoritesFolder.exists() && favoritesFolder.isReadable()) |
michael@0 | 146 | this.__favoritesFolder = favoritesFolder; |
michael@0 | 147 | } |
michael@0 | 148 | return this.__favoritesFolder; |
michael@0 | 149 | }, |
michael@0 | 150 | |
michael@0 | 151 | __toolbarFolderName: null, |
michael@0 | 152 | get _toolbarFolderName() { |
michael@0 | 153 | if (!this.__toolbarFolderName) { |
michael@0 | 154 | // Retrieve the name of IE's favorites subfolder that holds the bookmarks |
michael@0 | 155 | // in the toolbar. This was previously stored in the registry and changed |
michael@0 | 156 | // in IE7 to always be called "Links". |
michael@0 | 157 | let folderName = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
michael@0 | 158 | "Software\\Microsoft\\Internet Explorer\\Toolbar", |
michael@0 | 159 | "LinksFolderName"); |
michael@0 | 160 | this.__toolbarFolderName = folderName || "Links"; |
michael@0 | 161 | } |
michael@0 | 162 | return this.__toolbarFolderName; |
michael@0 | 163 | }, |
michael@0 | 164 | |
michael@0 | 165 | migrate: function B_migrate(aCallback) { |
michael@0 | 166 | PlacesUtils.bookmarks.runInBatchMode({ |
michael@0 | 167 | runBatched: (function migrateBatched() { |
michael@0 | 168 | // Import to the bookmarks menu. |
michael@0 | 169 | let destFolderId = PlacesUtils.bookmarksMenuFolderId; |
michael@0 | 170 | if (!MigrationUtils.isStartupMigration) { |
michael@0 | 171 | destFolderId = |
michael@0 | 172 | MigrationUtils.createImportedBookmarksFolder("IE", destFolderId); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | this._migrateFolder(this._favoritesFolder, destFolderId); |
michael@0 | 176 | |
michael@0 | 177 | aCallback(true); |
michael@0 | 178 | }).bind(this) |
michael@0 | 179 | }, null); |
michael@0 | 180 | }, |
michael@0 | 181 | |
michael@0 | 182 | _migrateFolder: function B__migrateFolder(aSourceFolder, aDestFolderId) { |
michael@0 | 183 | // TODO (bug 741993): the favorites order is stored in the Registry, at |
michael@0 | 184 | // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites |
michael@0 | 185 | // Until we support it, bookmarks are imported in alphabetical order. |
michael@0 | 186 | let entries = aSourceFolder.directoryEntries; |
michael@0 | 187 | while (entries.hasMoreElements()) { |
michael@0 | 188 | let entry = entries.getNext().QueryInterface(Ci.nsIFile); |
michael@0 | 189 | try { |
michael@0 | 190 | // Make sure that entry.path == entry.target to not follow .lnk folder |
michael@0 | 191 | // shortcuts which could lead to infinite cycles. |
michael@0 | 192 | // Don't use isSymlink(), since it would throw for invalid |
michael@0 | 193 | // lnk files pointing to URLs or to unresolvable paths. |
michael@0 | 194 | if (entry.path == entry.target && entry.isDirectory()) { |
michael@0 | 195 | let destFolderId; |
michael@0 | 196 | if (entry.leafName == this._toolbarFolderName && |
michael@0 | 197 | entry.parent.equals(this._favoritesFolder)) { |
michael@0 | 198 | // Import to the bookmarks toolbar. |
michael@0 | 199 | destFolderId = PlacesUtils.toolbarFolderId; |
michael@0 | 200 | if (!MigrationUtils.isStartupMigration) { |
michael@0 | 201 | destFolderId = |
michael@0 | 202 | MigrationUtils.createImportedBookmarksFolder("IE", destFolderId); |
michael@0 | 203 | } |
michael@0 | 204 | } |
michael@0 | 205 | else { |
michael@0 | 206 | // Import to a new folder. |
michael@0 | 207 | destFolderId = |
michael@0 | 208 | PlacesUtils.bookmarks.createFolder(aDestFolderId, entry.leafName, |
michael@0 | 209 | PlacesUtils.bookmarks.DEFAULT_INDEX); |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | if (entry.isReadable()) { |
michael@0 | 213 | // Recursively import the folder. |
michael@0 | 214 | this._migrateFolder(entry, destFolderId); |
michael@0 | 215 | } |
michael@0 | 216 | } |
michael@0 | 217 | else { |
michael@0 | 218 | // Strip the .url extension, to both check this is a valid link file, |
michael@0 | 219 | // and get the associated title. |
michael@0 | 220 | let matches = entry.leafName.match(/(.+)\.url$/i); |
michael@0 | 221 | if (matches) { |
michael@0 | 222 | let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]. |
michael@0 | 223 | getService(Ci.nsIFileProtocolHandler); |
michael@0 | 224 | let uri = fileHandler.readURLFile(entry); |
michael@0 | 225 | let title = matches[1]; |
michael@0 | 226 | |
michael@0 | 227 | PlacesUtils.bookmarks.insertBookmark(aDestFolderId, |
michael@0 | 228 | uri, |
michael@0 | 229 | PlacesUtils.bookmarks.DEFAULT_INDEX, |
michael@0 | 230 | title); |
michael@0 | 231 | } |
michael@0 | 232 | } |
michael@0 | 233 | } catch (ex) { |
michael@0 | 234 | Components.utils.reportError("Unable to import IE favorite (" + entry.leafName + "): " + ex); |
michael@0 | 235 | } |
michael@0 | 236 | } |
michael@0 | 237 | } |
michael@0 | 238 | }; |
michael@0 | 239 | |
michael@0 | 240 | function History() { |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | History.prototype = { |
michael@0 | 244 | type: MigrationUtils.resourceTypes.HISTORY, |
michael@0 | 245 | |
michael@0 | 246 | get exists() true, |
michael@0 | 247 | |
michael@0 | 248 | __typedURLs: null, |
michael@0 | 249 | get _typedURLs() { |
michael@0 | 250 | if (!this.__typedURLs) { |
michael@0 | 251 | // The list of typed URLs is a sort of annotation stored in the registry. |
michael@0 | 252 | // Currently, IE stores 25 entries and this value is not configurable, |
michael@0 | 253 | // but we just keep reading up to the first non-existing entry to support |
michael@0 | 254 | // possible future bumps of this limit. |
michael@0 | 255 | this.__typedURLs = {}; |
michael@0 | 256 | let registry = Cc["@mozilla.org/windows-registry-key;1"]. |
michael@0 | 257 | createInstance(Ci.nsIWindowsRegKey); |
michael@0 | 258 | try { |
michael@0 | 259 | registry.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
michael@0 | 260 | "Software\\Microsoft\\Internet Explorer\\TypedURLs", |
michael@0 | 261 | Ci.nsIWindowsRegKey.ACCESS_READ); |
michael@0 | 262 | for (let entry = 1; registry.hasValue("url" + entry); entry++) { |
michael@0 | 263 | let url = registry.readStringValue("url" + entry); |
michael@0 | 264 | this.__typedURLs[url] = true; |
michael@0 | 265 | } |
michael@0 | 266 | } catch (ex) { |
michael@0 | 267 | } finally { |
michael@0 | 268 | registry.close(); |
michael@0 | 269 | } |
michael@0 | 270 | } |
michael@0 | 271 | return this.__typedURLs; |
michael@0 | 272 | }, |
michael@0 | 273 | |
michael@0 | 274 | migrate: function H_migrate(aCallback) { |
michael@0 | 275 | let places = []; |
michael@0 | 276 | let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"]. |
michael@0 | 277 | createInstance(Ci.nsISimpleEnumerator); |
michael@0 | 278 | while (historyEnumerator.hasMoreElements()) { |
michael@0 | 279 | let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2); |
michael@0 | 280 | let uri = entry.get("uri").QueryInterface(Ci.nsIURI); |
michael@0 | 281 | // MSIE stores some types of URLs in its history that we don't handle, |
michael@0 | 282 | // like HTMLHelp and others. Since we don't properly map handling for |
michael@0 | 283 | // all of them we just avoid importing them. |
michael@0 | 284 | if (["http", "https", "ftp", "file"].indexOf(uri.scheme) == -1) { |
michael@0 | 285 | continue; |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | let title = entry.get("title"); |
michael@0 | 289 | // Embed visits have no title and don't need to be imported. |
michael@0 | 290 | if (title.length == 0) { |
michael@0 | 291 | continue; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | // The typed urls are already fixed-up, so we can use them for comparison. |
michael@0 | 295 | let transitionType = this._typedURLs[uri.spec] ? |
michael@0 | 296 | Ci.nsINavHistoryService.TRANSITION_TYPED : |
michael@0 | 297 | Ci.nsINavHistoryService.TRANSITION_LINK; |
michael@0 | 298 | let lastVisitTime = entry.get("time"); |
michael@0 | 299 | |
michael@0 | 300 | places.push( |
michael@0 | 301 | { uri: uri, |
michael@0 | 302 | title: title, |
michael@0 | 303 | visits: [{ transitionType: transitionType, |
michael@0 | 304 | visitDate: lastVisitTime }] |
michael@0 | 305 | } |
michael@0 | 306 | ); |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | // Check whether there is any history to import. |
michael@0 | 310 | if (places.length == 0) { |
michael@0 | 311 | aCallback(true); |
michael@0 | 312 | return; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | PlacesUtils.asyncHistory.updatePlaces(places, { |
michael@0 | 316 | _success: false, |
michael@0 | 317 | handleResult: function() { |
michael@0 | 318 | // Importing any entry is considered a successful import. |
michael@0 | 319 | this._success = true; |
michael@0 | 320 | }, |
michael@0 | 321 | handleError: function() {}, |
michael@0 | 322 | handleCompletion: function() { |
michael@0 | 323 | aCallback(this._success); |
michael@0 | 324 | } |
michael@0 | 325 | }); |
michael@0 | 326 | } |
michael@0 | 327 | }; |
michael@0 | 328 | |
michael@0 | 329 | function Cookies() { |
michael@0 | 330 | } |
michael@0 | 331 | |
michael@0 | 332 | Cookies.prototype = { |
michael@0 | 333 | type: MigrationUtils.resourceTypes.COOKIES, |
michael@0 | 334 | |
michael@0 | 335 | get exists() !!this._cookiesFolder, |
michael@0 | 336 | |
michael@0 | 337 | __cookiesFolder: null, |
michael@0 | 338 | get _cookiesFolder() { |
michael@0 | 339 | // Cookies are stored in txt files, in a Cookies folder whose path varies |
michael@0 | 340 | // across the different OS versions. CookD takes care of most of these |
michael@0 | 341 | // cases, though, in Windows Vista/7, UAC makes a difference. |
michael@0 | 342 | // If UAC is enabled, the most common destination is CookD/Low. Though, |
michael@0 | 343 | // if the user runs the application in administrator mode or disables UAC, |
michael@0 | 344 | // cookies are stored in the original CookD destination. Cause running the |
michael@0 | 345 | // browser in administrator mode is unsafe and discouraged, we just care |
michael@0 | 346 | // about the UAC state. |
michael@0 | 347 | if (!this.__cookiesFolder) { |
michael@0 | 348 | let cookiesFolder = Services.dirsvc.get("CookD", Ci.nsIFile); |
michael@0 | 349 | if (cookiesFolder.exists() && cookiesFolder.isReadable()) { |
michael@0 | 350 | // Check if UAC is enabled. |
michael@0 | 351 | if (Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).userCanElevate) { |
michael@0 | 352 | cookiesFolder.append("Low"); |
michael@0 | 353 | } |
michael@0 | 354 | this.__cookiesFolder = cookiesFolder; |
michael@0 | 355 | } |
michael@0 | 356 | } |
michael@0 | 357 | return this.__cookiesFolder; |
michael@0 | 358 | }, |
michael@0 | 359 | |
michael@0 | 360 | migrate: function C_migrate(aCallback) { |
michael@0 | 361 | CtypesHelpers.initialize(); |
michael@0 | 362 | |
michael@0 | 363 | let cookiesGenerator = (function genCookie() { |
michael@0 | 364 | let success = false; |
michael@0 | 365 | let entries = this._cookiesFolder.directoryEntries; |
michael@0 | 366 | while (entries.hasMoreElements()) { |
michael@0 | 367 | let entry = entries.getNext().QueryInterface(Ci.nsIFile); |
michael@0 | 368 | // Skip eventual bogus entries. |
michael@0 | 369 | if (!entry.isFile() || !/\.txt$/.test(entry.leafName)) |
michael@0 | 370 | continue; |
michael@0 | 371 | |
michael@0 | 372 | this._readCookieFile(entry, function(aSuccess) { |
michael@0 | 373 | // Importing even a single cookie file is considered a success. |
michael@0 | 374 | if (aSuccess) |
michael@0 | 375 | success = true; |
michael@0 | 376 | try { |
michael@0 | 377 | cookiesGenerator.next(); |
michael@0 | 378 | } catch (ex) {} |
michael@0 | 379 | }); |
michael@0 | 380 | |
michael@0 | 381 | yield undefined; |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | CtypesHelpers.finalize(); |
michael@0 | 385 | |
michael@0 | 386 | aCallback(success); |
michael@0 | 387 | }).apply(this); |
michael@0 | 388 | cookiesGenerator.next(); |
michael@0 | 389 | }, |
michael@0 | 390 | |
michael@0 | 391 | _readCookieFile: function C__readCookieFile(aFile, aCallback) { |
michael@0 | 392 | let fileReader = Cc["@mozilla.org/files/filereader;1"]. |
michael@0 | 393 | createInstance(Ci.nsIDOMFileReader); |
michael@0 | 394 | fileReader.addEventListener("loadend", (function onLoadEnd() { |
michael@0 | 395 | fileReader.removeEventListener("loadend", onLoadEnd, false); |
michael@0 | 396 | |
michael@0 | 397 | if (fileReader.readyState != fileReader.DONE) { |
michael@0 | 398 | Cu.reportError("Could not read cookie contents: " + fileReader.error); |
michael@0 | 399 | aCallback(false); |
michael@0 | 400 | return; |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | let success = true; |
michael@0 | 404 | try { |
michael@0 | 405 | this._parseCookieBuffer(fileReader.result); |
michael@0 | 406 | } catch (ex) { |
michael@0 | 407 | Components.utils.reportError("Unable to migrate cookie: " + ex); |
michael@0 | 408 | success = false; |
michael@0 | 409 | } finally { |
michael@0 | 410 | aCallback(success); |
michael@0 | 411 | } |
michael@0 | 412 | }).bind(this), false); |
michael@0 | 413 | fileReader.readAsText(File(aFile)); |
michael@0 | 414 | }, |
michael@0 | 415 | |
michael@0 | 416 | /** |
michael@0 | 417 | * Parses a cookie file buffer and returns an array of the contained cookies. |
michael@0 | 418 | * |
michael@0 | 419 | * The cookie file format is a newline-separated-values with a "*" used as |
michael@0 | 420 | * delimeter between multiple records. |
michael@0 | 421 | * Each cookie has the following fields: |
michael@0 | 422 | * - name |
michael@0 | 423 | * - value |
michael@0 | 424 | * - host/path |
michael@0 | 425 | * - flags |
michael@0 | 426 | * - Expiration time most significant integer |
michael@0 | 427 | * - Expiration time least significant integer |
michael@0 | 428 | * - Creation time most significant integer |
michael@0 | 429 | * - Creation time least significant integer |
michael@0 | 430 | * - Record delimiter "*" |
michael@0 | 431 | * |
michael@0 | 432 | * @note All the times are in FILETIME format. |
michael@0 | 433 | */ |
michael@0 | 434 | _parseCookieBuffer: function C__parseCookieBuffer(aTextBuffer) { |
michael@0 | 435 | // Note the last record is an empty string. |
michael@0 | 436 | let records = [r for each (r in aTextBuffer.split("*\n")) if (r)]; |
michael@0 | 437 | for (let record of records) { |
michael@0 | 438 | let [name, value, hostpath, flags, |
michael@0 | 439 | expireTimeLo, expireTimeHi] = record.split("\n"); |
michael@0 | 440 | |
michael@0 | 441 | // IE stores deleted cookies with a zero-length value, skip them. |
michael@0 | 442 | if (value.length == 0) |
michael@0 | 443 | continue; |
michael@0 | 444 | |
michael@0 | 445 | let hostLen = hostpath.indexOf("/"); |
michael@0 | 446 | let host = hostpath.substr(0, hostLen); |
michael@0 | 447 | |
michael@0 | 448 | // For a non-null domain, assume it's what Mozilla considers |
michael@0 | 449 | // a domain cookie. See bug 222343. |
michael@0 | 450 | if (host.length > 0) { |
michael@0 | 451 | // Fist delete any possible extant matching host cookie. |
michael@0 | 452 | Services.cookies.remove(host, name, path, false); |
michael@0 | 453 | // Now make it a domain cookie. |
michael@0 | 454 | if (host[0] != "." && !hostIsIPAddress(host)) |
michael@0 | 455 | host = "." + host; |
michael@0 | 456 | } |
michael@0 | 457 | |
michael@0 | 458 | let path = hostpath.substr(hostLen); |
michael@0 | 459 | let expireTime = CtypesHelpers.fileTimeToDate(Number(expireTimeHi), |
michael@0 | 460 | Number(expireTimeLo)); |
michael@0 | 461 | Services.cookies.add(host, |
michael@0 | 462 | path, |
michael@0 | 463 | name, |
michael@0 | 464 | value, |
michael@0 | 465 | Number(flags) & 0x1, // secure |
michael@0 | 466 | false, // httpOnly |
michael@0 | 467 | false, // session |
michael@0 | 468 | expireTime); |
michael@0 | 469 | } |
michael@0 | 470 | } |
michael@0 | 471 | }; |
michael@0 | 472 | |
michael@0 | 473 | function Settings() { |
michael@0 | 474 | } |
michael@0 | 475 | |
michael@0 | 476 | Settings.prototype = { |
michael@0 | 477 | type: MigrationUtils.resourceTypes.SETTINGS, |
michael@0 | 478 | |
michael@0 | 479 | get exists() true, |
michael@0 | 480 | |
michael@0 | 481 | migrate: function S_migrate(aCallback) { |
michael@0 | 482 | // Converts from yes/no to a boolean. |
michael@0 | 483 | function yesNoToBoolean(v) v == "yes"; |
michael@0 | 484 | |
michael@0 | 485 | // Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into |
michael@0 | 486 | // destination format like "en-us, ar-kw, ar-om". |
michael@0 | 487 | // Final string is sorted by quality (q=) param. |
michael@0 | 488 | function parseAcceptLanguageList(v) { |
michael@0 | 489 | return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi) |
michael@0 | 490 | .sort(function (a , b) { |
michael@0 | 491 | let qA = parseFloat(a.split(";q=")[1]) || 1.0; |
michael@0 | 492 | let qB = parseFloat(b.split(";q=")[1]) || 1.0; |
michael@0 | 493 | return qA < qB ? 1 : qA == qB ? 0 : -1; |
michael@0 | 494 | }) |
michael@0 | 495 | .map(function(a) a.split(";")[0]); |
michael@0 | 496 | } |
michael@0 | 497 | |
michael@0 | 498 | // For reference on some of the available IE Registry settings: |
michael@0 | 499 | // * http://msdn.microsoft.com/en-us/library/cc980058%28v=prot.13%29.aspx |
michael@0 | 500 | // * http://msdn.microsoft.com/en-us/library/cc980059%28v=prot.13%29.aspx |
michael@0 | 501 | |
michael@0 | 502 | // Note that only settings exposed in our UI should be migrated. |
michael@0 | 503 | |
michael@0 | 504 | this._set("Software\\Microsoft\\Internet Explorer\\International", |
michael@0 | 505 | "AcceptLanguage", |
michael@0 | 506 | "intl.accept_languages", |
michael@0 | 507 | parseAcceptLanguageList); |
michael@0 | 508 | // TODO (bug 745853): For now, only x-western font is translated. |
michael@0 | 509 | this._set("Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3", |
michael@0 | 510 | "IEFixedFontName", |
michael@0 | 511 | "font.name.monospace.x-western"); |
michael@0 | 512 | this._set(kMainKey, |
michael@0 | 513 | "Use FormSuggest", |
michael@0 | 514 | "browser.formfill.enable", |
michael@0 | 515 | yesNoToBoolean); |
michael@0 | 516 | this._set(kMainKey, |
michael@0 | 517 | "FormSuggest Passwords", |
michael@0 | 518 | "signon.rememberSignons", |
michael@0 | 519 | yesNoToBoolean); |
michael@0 | 520 | this._set(kMainKey, |
michael@0 | 521 | "Anchor Underline", |
michael@0 | 522 | "browser.underline_anchors", |
michael@0 | 523 | yesNoToBoolean); |
michael@0 | 524 | this._set(kMainKey, |
michael@0 | 525 | "Display Inline Images", |
michael@0 | 526 | "permissions.default.image", |
michael@0 | 527 | function (v) yesNoToBoolean(v) ? 1 : 2); |
michael@0 | 528 | this._set(kMainKey, |
michael@0 | 529 | "Move System Caret", |
michael@0 | 530 | "accessibility.browsewithcaret", |
michael@0 | 531 | yesNoToBoolean); |
michael@0 | 532 | this._set("Software\\Microsoft\\Internet Explorer\\Settings", |
michael@0 | 533 | "Always Use My Colors", |
michael@0 | 534 | "browser.display.use_document_colors", |
michael@0 | 535 | function (v) !Boolean(v)); |
michael@0 | 536 | this._set("Software\\Microsoft\\Internet Explorer\\Settings", |
michael@0 | 537 | "Always Use My Font Face", |
michael@0 | 538 | "browser.display.use_document_fonts", |
michael@0 | 539 | function (v) !Boolean(v)); |
michael@0 | 540 | this._set(kMainKey, |
michael@0 | 541 | "SmoothScroll", |
michael@0 | 542 | "general.smoothScroll", |
michael@0 | 543 | Boolean); |
michael@0 | 544 | this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\", |
michael@0 | 545 | "WarnOnClose", |
michael@0 | 546 | "browser.tabs.warnOnClose", |
michael@0 | 547 | Boolean); |
michael@0 | 548 | this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\", |
michael@0 | 549 | "OpenInForeground", |
michael@0 | 550 | "browser.tabs.loadInBackground", |
michael@0 | 551 | function (v) !Boolean(v)); |
michael@0 | 552 | |
michael@0 | 553 | aCallback(true); |
michael@0 | 554 | }, |
michael@0 | 555 | |
michael@0 | 556 | /** |
michael@0 | 557 | * Reads a setting from the Registry and stores the converted result into |
michael@0 | 558 | * the appropriate Firefox preference. |
michael@0 | 559 | * |
michael@0 | 560 | * @param aPath |
michael@0 | 561 | * Registry path under HKCU. |
michael@0 | 562 | * @param aKey |
michael@0 | 563 | * Name of the key. |
michael@0 | 564 | * @param aPref |
michael@0 | 565 | * Firefox preference. |
michael@0 | 566 | * @param [optional] aTransformFn |
michael@0 | 567 | * Conversion function from the Registry format to the pref format. |
michael@0 | 568 | */ |
michael@0 | 569 | _set: function S__set(aPath, aKey, aPref, aTransformFn) { |
michael@0 | 570 | let value = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
michael@0 | 571 | aPath, aKey); |
michael@0 | 572 | // Don't import settings that have never been flipped. |
michael@0 | 573 | if (value === undefined) |
michael@0 | 574 | return; |
michael@0 | 575 | |
michael@0 | 576 | if (aTransformFn) |
michael@0 | 577 | value = aTransformFn(value); |
michael@0 | 578 | |
michael@0 | 579 | switch (typeof(value)) { |
michael@0 | 580 | case "string": |
michael@0 | 581 | Services.prefs.setCharPref(aPref, value); |
michael@0 | 582 | break; |
michael@0 | 583 | case "number": |
michael@0 | 584 | Services.prefs.setIntPref(aPref, value); |
michael@0 | 585 | break; |
michael@0 | 586 | case "boolean": |
michael@0 | 587 | Services.prefs.setBoolPref(aPref, value); |
michael@0 | 588 | break; |
michael@0 | 589 | default: |
michael@0 | 590 | throw new Error("Unexpected value type: " + typeof(value)); |
michael@0 | 591 | } |
michael@0 | 592 | } |
michael@0 | 593 | }; |
michael@0 | 594 | |
michael@0 | 595 | //////////////////////////////////////////////////////////////////////////////// |
michael@0 | 596 | //// Migrator |
michael@0 | 597 | |
michael@0 | 598 | function IEProfileMigrator() |
michael@0 | 599 | { |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | IEProfileMigrator.prototype = Object.create(MigratorPrototype); |
michael@0 | 603 | |
michael@0 | 604 | IEProfileMigrator.prototype.getResources = function IE_getResources() { |
michael@0 | 605 | let resources = [ |
michael@0 | 606 | new Bookmarks() |
michael@0 | 607 | , new History() |
michael@0 | 608 | , new Cookies() |
michael@0 | 609 | , new Settings() |
michael@0 | 610 | ]; |
michael@0 | 611 | return [r for each (r in resources) if (r.exists)]; |
michael@0 | 612 | }; |
michael@0 | 613 | |
michael@0 | 614 | Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", { |
michael@0 | 615 | get: function IE_get_sourceHomePageURL() { |
michael@0 | 616 | let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, |
michael@0 | 617 | kMainKey, "Default_Page_URL"); |
michael@0 | 618 | let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
michael@0 | 619 | kMainKey, "Start Page"); |
michael@0 | 620 | // If the user didn't customize the Start Page, he is still on the default |
michael@0 | 621 | // page, that may be considered the equivalent of our about:home. There's |
michael@0 | 622 | // no reason to retain it, since it is heavily targeted to IE. |
michael@0 | 623 | let homepage = startPage != defaultStartPage ? startPage : ""; |
michael@0 | 624 | |
michael@0 | 625 | // IE7+ supports secondary home pages located in a REG_MULTI_SZ key. These |
michael@0 | 626 | // are in addition to the Start Page, and no empty entries are possible, |
michael@0 | 627 | // thus a Start Page is always defined if any of these exists, though it |
michael@0 | 628 | // may be the default one. |
michael@0 | 629 | let secondaryPages = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, |
michael@0 | 630 | kMainKey, "Secondary Start Pages"); |
michael@0 | 631 | if (secondaryPages) { |
michael@0 | 632 | if (homepage) |
michael@0 | 633 | secondaryPages.unshift(homepage); |
michael@0 | 634 | homepage = secondaryPages.join("|"); |
michael@0 | 635 | } |
michael@0 | 636 | |
michael@0 | 637 | return homepage; |
michael@0 | 638 | } |
michael@0 | 639 | }); |
michael@0 | 640 | |
michael@0 | 641 | IEProfileMigrator.prototype.classDescription = "IE Profile Migrator"; |
michael@0 | 642 | IEProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=ie"; |
michael@0 | 643 | IEProfileMigrator.prototype.classID = Components.ID("{3d2532e3-4932-4774-b7ba-968f5899d3a4}"); |
michael@0 | 644 | |
michael@0 | 645 | this.NSGetFactory = XPCOMUtils.generateNSGetFactory([IEProfileMigrator]); |