browser/components/migration/src/IEProfileMigrator.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial