Merge last PGO-green changeset of mozilla-inbound to mozilla-central a=merge
authorEd Morley <emorley@mozilla.com>
Tue, 17 Apr 2012 17:47:09 -0700
changeset 91888 93dfd98900ad8e01e64e0bafb8880209777c5811
parent 91843 719a2fb28324ec55ab92c776c31c5ef4517ed91e (current diff)
parent 91887 6b917c007510a7c4f7c75d542a9419bbf396c80a (diff)
child 91889 0c7e2911be75b12aa98e83800231cecf127ec997
push id690
push usertim.taubert@gmx.de
push dateThu, 19 Apr 2012 08:49:11 +0000
treeherderfx-team@a9b543de6b67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone14.0a1
Merge last PGO-green changeset of mozilla-inbound to mozilla-central a=merge
browser/base/content/browser.js
browser/base/content/browser.xul
browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
browser/components/migration/src/nsBrowserProfileMigratorUtils.h
browser/components/migration/src/nsIEProfileMigrator.cpp
browser/components/migration/src/nsIEProfileMigrator.h
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -33,21 +33,19 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "mozilla/Util.h"
-
-#include "nsAccessible.h"
 #include "nsAccessibleWrap.h"
 
+#include "Accessible-inl.h"
 #include "InterfaceInitFuncs.h"
 #include "nsAccUtils.h"
 #include "nsApplicationAccessibleWrap.h"
 #include "nsIAccessibleRelation.h"
 #include "nsRootAccessible.h"
 #include "nsDocAccessibleWrap.h"
 #include "nsIAccessibleValue.h"
 #include "nsMai.h"
@@ -55,16 +53,17 @@
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "prprf.h"
 #include "nsRoleMap.h"
 #include "nsStateMap.h"
 #include "Relation.h"
 #include "States.h"
 
+#include "mozilla/Util.h"
 #include "nsXPCOMStrings.h"
 #include "nsComponentManagerUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 nsAccessibleWrap::EAvailableAtkSignals nsAccessibleWrap::gAvailableAtkSignals =
   eUnknown;
--- a/accessible/src/atk/nsMaiInterfaceAction.cpp
+++ b/accessible/src/atk/nsMaiInterfaceAction.cpp
@@ -34,16 +34,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "InterfaceInitFuncs.h"
 
+#include "Accessible-inl.h"
 #include "nsMai.h"
 #include "Role.h"
 
 #include "nsString.h"
 
 using namespace mozilla::a11y;
 
 extern "C" {
--- a/accessible/src/jsat/Presenters.jsm
+++ b/accessible/src/jsat/Presenters.jsm
@@ -19,21 +19,16 @@ var EXPORTED_SYMBOLS = ['VisualPresenter
 /**
  * The interface for all presenter classes. A presenter could be, for example,
  * a speech output module, or a visual cursor indicator.
  */
 function Presenter() {}
 
 Presenter.prototype = {
   /**
-   * The padding in pixels between the object and the highlight border.
-   */
-  BORDER_PADDING: 2,
-
-  /**
    * Attach function for presenter.
    * @param {ChromeWindow} aWindow Chrome window the presenter could use.
    */
   attach: function attach(aWindow) {},
 
   /**
    * Detach function.
    */
@@ -91,16 +86,21 @@ Presenter.prototype = {
 /**
  * Visual presenter. Draws a box around the virtual cursor's position.
  */
 
 function VisualPresenter() {}
 
 VisualPresenter.prototype = new Presenter();
 
+/**
+ * The padding in pixels between the object and the highlight border.
+ */
+VisualPresenter.prototype.BORDER_PADDING = 2;
+
 VisualPresenter.prototype.attach = function(aWindow) {
   this.chromeWin = aWindow;
 
   // Add stylesheet
   let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
   this.stylesheet = aWindow.document.createProcessingInstruction(
     'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
   aWindow.document.insertBefore(this.stylesheet, aWindow.document.firstChild);
@@ -246,17 +246,17 @@ AndroidPresenter.prototype.tabSelected =
   let vcDoc = aObject.QueryInterface(Ci.nsIAccessibleCursorable);
   let context = [];
 
   let parent = vcDoc.virtualCursor.position || aObject;
   while ((parent = parent.parent))
     context.push(parent);
   context.reverse();
 
-  this.pivotChanged(vcDoc.virtualCursor.position, context);
+  this.pivotChanged(vcDoc.virtualCursor.position || aObject, context);
 };
 
 AndroidPresenter.prototype.sendMessageToJava = function(aMessage) {
   return Cc['@mozilla.org/android/bridge;1'].
     getService(Ci.nsIAndroidBridge).
     handleGeckoMessage(JSON.stringify(aMessage));
 };
 
--- a/accessible/tests/mochitest/actions/test_keys.html
+++ b/accessible/tests/mochitest/actions/test_keys.html
@@ -21,18 +21,18 @@
 
       is(acc.keyboardShortcut, aKey,
          "Wrong keyboard shortcut on " + prettyName(aAccOrElmOrID));
     }
 
     function doTest()
     {
       testKeyboardShortcut("input1", "");
-      testKeyboardShortcut("input2", MAC ? "⌃b" : "Alt+Shift+b");
-      testKeyboardShortcut("link", MAC ? "⌃l" : "Alt+Shift+l");
+      testKeyboardShortcut("input2", MAC ? "⌃%b" : "Alt+Shift+b");
+      testKeyboardShortcut("link", MAC ? "⌃%l" : "Alt+Shift+l");
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/attributes.js
+++ b/accessible/tests/mochitest/attributes.js
@@ -232,18 +232,17 @@ const kInputFontFamily =
 
 const kMonospaceFontFamily =
   function(aFontFamily) { return aFontFamily != "monospace"; }
 const kSansSerifFontFamily =
   function(aFontFamily) { return aFontFamily != "sans-serif"; }
 const kSerifFontFamily =
   function(aFontFamily) { return aFontFamily != "serif"; }
 
-const kCursiveFontFamily = WIN ? "Comic Sans MS" :
-  (LINUX ? "DejaVu Serif" : "MacFont");
+const kCursiveFontFamily = LINUX ? "DejaVu Serif" : "Comic Sans MS";
 
 /**
  * Return used font from the given computed style.
  */
 function fontFamily(aComputedStyle)
 {
   var name = aComputedStyle.fontFamily;
   switch (name) {
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -205,28 +205,28 @@
     <markup ref="html:img" ruleset="htmlimage">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                 aria-label="Logo of Mozilla"
                 aria-labelledby="l1 l2"
                 alt="Mozilla logo"
                 title="This is a logo"
-                src="moz.png"/>
+                src="../moz.png"/>
     </markup>
 
     <markup ref="html:img" ruleset="htmlimageemptyalt">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:img id="img"
                  aria-label="Logo of Mozilla"
                  aria-labelledby="l1 l2"
                  title="This is a logo"
                  alt=""
-                 src="moz.png"/>
+                 src="../moz.png"/>
     </markup>
 
     <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
             id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -35,16 +35,22 @@ config/autoconf.mk
 config/nspr/Makefile
 config/doxygen.cfg
 config/expandlibs_config.py
 mfbt/Makefile
 probes/Makefile
 extensions/Makefile
 "
 
+if [ "$MOZ_WEBAPP_RUNTIME" ]; then
+  add_makefiles "
+webapprt/Makefile
+  "
+fi
+
 if [ ! "$LIBXUL_SDK" ]; then
   if [ "$STLPORT_SOURCES" ]; then
     add_makefiles "
       build/stlport/Makefile
       build/stlport/stl/config/_android.h
     "
   fi
   add_makefiles "
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -344,16 +344,22 @@ pref("browser.download.manager.showWhenS
 pref("browser.download.manager.closeWhenDone", false);
 pref("browser.download.manager.focusWhenStarting", false);
 pref("browser.download.manager.flashCount", 2);
 pref("browser.download.manager.addToRecentDocs", true);
 pref("browser.download.manager.quitBehavior", 0);
 pref("browser.download.manager.scanWhenDone", true);
 pref("browser.download.manager.resumeOnWakeDelay", 10000);
 
+// This allows disabling the Downloads Panel in favor of the old interface.
+pref("browser.download.useToolkitUI", false);
+
+// This controls retention behavior in the Downloads Panel only.
+pref("browser.download.panel.removeFinishedDownloads", false);
+
 // search engines URL
 pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
 
 // pointer to the default engine name
 pref("browser.search.defaultenginename",      "chrome://browser-region/locale/region.properties");
 
 // disable logging for the search service by default
 pref("browser.search.log", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1386,16 +1386,18 @@ function BrowserStartup() {
   TabsOnTop.init();
 
   BookmarksMenuButton.init();
 
   TabsInTitlebar.init();
 
   gPrivateBrowsingUI.init();
 
+  DownloadsButton.initializePlaceholder();
+
   retrieveToolbarIconsizesFromTheme();
 
   gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
   gStartupRan = true;
 }
 
 function HandleAppCommandEvent(evt) {
   evt.stopPropagation();
@@ -1635,16 +1637,21 @@ function delayedStartup(isLoadingBlank, 
       let tempScope = {};
       Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
                 tempScope);
       tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
     }
 #endif
   }, 10000);
 
+  // The object handling the downloads indicator is also initialized here in the
+  // delayed startup function, but the actual indicator element is not loaded
+  // unless there are downloads to be displayed.
+  DownloadsButton.initializeIndicator();
+
 #ifndef XP_MACOSX
   updateEditUIVisibility();
   let placesContext = document.getElementById("placesContext");
   placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
   placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
 #endif
 
   gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
@@ -3717,16 +3724,17 @@ function BrowserCustomizeToolbar()
   var splitter = document.getElementById("urlbar-search-splitter");
   if (splitter)
     splitter.parentNode.removeChild(splitter);
 
   CombinedStopReload.uninit();
 
   PlacesToolbarHelper.customizeStart();
   BookmarksMenuButton.customizeStart();
+  DownloadsButton.customizeStart();
 
   TabsInTitlebar.allowedBy("customizing-toolbars", false);
 
   var customizeURL = "chrome://global/content/customizeToolbar.xul";
   gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
 
   if (gCustomizeSheet) {
     var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
@@ -3783,16 +3791,17 @@ function BrowserToolboxCustomizeDone(aTo
     // if it already exists, since it may have changed if the URL bar was
     // added/removed.
     if (!__lookupGetter__("PopupNotifications"))
       PopupNotifications.iconBox = document.getElementById("notification-popup-box");
   }
 
   PlacesToolbarHelper.customizeDone();
   BookmarksMenuButton.customizeDone();
+  DownloadsButton.customizeDone();
 
   // The url bar splitter state is dependent on whether stop/reload
   // and the location bar are combined, so we need this ordering
   CombinedStopReload.init();
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -850,16 +850,19 @@
 
 # Update primaryToolbarButtons in browser/themes/browserShared.inc when adding
 # or removing default items with the toolbarbutton-1 class.
 
       <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      label="&printButton.label;" command="cmd_print"
                      tooltiptext="&printButton.tooltip;"/>
 
+      <!-- This is a placeholder for the Downloads Indicator.  It is visible
+           only during the customization of the toolbar or in the palette, and
+           is replaced when customization is done. -->
       <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      observes="Tools:Downloads"
                      ondrop="DownloadsButtonDNDObserver.onDrop(event)"
                      ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
                      ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
                      ondragexit="DownloadsButtonDNDObserver.onDragExit(event)"
                      label="&downloads.label;"
                      tooltiptext="&downloads.tooltip;"/>
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -36,10 +36,12 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
 <script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
 <script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser.js"/>
+<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
+<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
 <script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
 <script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -46,16 +46,20 @@ tier_app_dirs += extensions
 endif
 
 tier_app_dirs += $(MOZ_BRANDING_DIRECTORY)
 
 ifdef MOZ_SERVICES_SYNC
 tier_app_dirs += services
 endif
 
+ifdef MOZ_WEBAPP_RUNTIME
+tier_app_dirs += webapprt
+endif
+
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
 
 ################################################
 # Parallel build on Windows with GNU make check
 
 default::
--- a/browser/components/BrowserComponents.manifest
+++ b/browser/components/BrowserComponents.manifest
@@ -19,13 +19,27 @@ contract @mozilla.org/uriloader/content-
 contract @mozilla.org/uriloader/content-handler;1?type=image/x-icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 contract @mozilla.org/uriloader/content-handler;1?type=image/vnd.microsoft.icon {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 contract @mozilla.org/uriloader/content-handler;1?type=application/http-index-format {5d0ce354-df01-421a-83fb-7ead0990c24e} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-handler m-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-handler x-default @mozilla.org/browser/final-clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 category command-line-validator b-browser @mozilla.org/browser/clh;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 
 # nsBrowserGlue.js
+
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js
 contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc}
-category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1
+category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
 component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js
 contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a}
--- a/browser/components/Makefile.in
+++ b/browser/components/Makefile.in
@@ -57,16 +57,17 @@ EXTRA_PP_COMPONENTS = \
   $(NULL)
 
 EXTRA_JS_MODULES = distribution.js
 
 PARALLEL_DIRS = \
   about \
   certerror \
   dirprovider \
+  downloads \
   feeds \
   places \
   preferences \
   privatebrowsing \
   search \
   sessionstore \
   shell \
   sidebar \
--- a/browser/components/build/nsBrowserCompsCID.h
+++ b/browser/components/build/nsBrowserCompsCID.h
@@ -31,34 +31,24 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /////////////////////////////////////////////////////////////////////////////
 
-#define NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "@mozilla.org/profile/migrator;1?app=browser&type="
-
 #ifdef XP_WIN
-#define NS_WINIEPROFILEMIGRATOR_CID \
-{ 0xbc15c73d, 0xc05b, 0x497b, { 0xa3, 0x73, 0x4b, 0xae, 0x6c, 0x17, 0x86, 0x31 } }
-
 #define NS_WINIEHISTORYENUMERATOR_CID \
 { 0x93480624, 0x806e, 0x4756, { 0xb7, 0xcb, 0x0f, 0xb7, 0xdd, 0x74, 0x6a, 0x8f } }
 
 #define NS_IEHISTORYENUMERATOR_CONTRACTID \
   "@mozilla.org/profile/migrator/iehistoryenumerator;1"
 #endif
 
-#ifdef XP_MACOSX
-#define NS_SAFARIPROFILEMIGRATOR_CID \
-{ 0x29e3b139, 0xad19, 0x44f3, { 0xb2, 0xc2, 0xe9, 0xf1, 0x3b, 0xa2, 0xbb, 0xc6 } }
-#endif
-
 #define NS_SHELLSERVICE_CID \
 { 0x63c7b9f4, 0xcc8, 0x43f8, { 0xb6, 0x66, 0xa, 0x66, 0x16, 0x55, 0xcb, 0x73 } }
 
 #define NS_SHELLSERVICE_CONTRACTID \
   "@mozilla.org/browser/shell-service;1"
 
 #define NS_RDF_FORWARDPROXY_INFER_DATASOURCE_CID \
 { 0x7a024bcf, 0xedd5, 0x4d9a, { 0x86, 0x14, 0xd4, 0x4b, 0xe1, 0xda, 0xda, 0xd3 } }
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -45,17 +45,16 @@
 #include "nsWindowsShellService.h"
 #elif defined(XP_MACOSX)
 #include "nsMacShellService.h"
 #elif defined(MOZ_WIDGET_GTK2)
 #include "nsGNOMEShellService.h"
 #endif
 
 #if defined(XP_WIN) && !defined(__MINGW32__)
-#include "nsIEProfileMigrator.h"
 #include "nsIEHistoryEnumerator.h"
 #endif
 
 #include "rdf.h"
 #include "nsFeedSniffer.h"
 #include "AboutRedirector.h"
 #include "nsIAboutModule.h"
 
@@ -71,51 +70,48 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Directory
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
 #elif defined(XP_MACOSX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService)
 #elif defined(MOZ_WIDGET_GTK2)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
 #endif
 
 #if defined(XP_WIN) && !defined(__MINGW32__)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsIEProfileMigrator)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIEHistoryEnumerator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrivateBrowsingServiceWrapper, Init)
 
 NS_DEFINE_NAMED_CID(NS_BROWSERDIRECTORYPROVIDER_CID);
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #elif defined(MOZ_WIDGET_GTK2)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FEEDSNIFFER_CID);
 NS_DEFINE_NAMED_CID(NS_BROWSER_ABOUT_REDIRECTOR_CID);
 #if defined(XP_WIN) && !defined(__MINGW32__)
-NS_DEFINE_NAMED_CID(NS_WINIEPROFILEMIGRATOR_CID);
 NS_DEFINE_NAMED_CID(NS_WINIEHISTORYENUMERATOR_CID);
 #elif defined(XP_MACOSX)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID);
 
 static const mozilla::Module::CIDEntry kBrowserCIDs[] = {
     { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor },
 #if defined(XP_WIN)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
 #elif defined(MOZ_WIDGET_GTK2)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
 #endif
     { &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
     { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create },
 #if defined(XP_WIN) && !defined(__MINGW32__)
-    { &kNS_WINIEPROFILEMIGRATOR_CID, false, NULL, nsIEProfileMigratorConstructor },
     { &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor },
 #elif defined(XP_MACOSX)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
 #endif
     { &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID, false, NULL, nsPrivateBrowsingServiceWrapperConstructor },
     { NULL }
 };
 
@@ -139,17 +135,16 @@ static const mozilla::Module::ContractID
 #ifdef MOZ_SERVICES_SYNC
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN) && !defined(__MINGW32__)
-    { NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "ie", &kNS_WINIEPROFILEMIGRATOR_CID },
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
     { NS_PRIVATE_BROWSING_SERVICE_CONTRACTID, &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID },
     { NULL }
 };
 
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/Makefile.in
@@ -0,0 +1,18 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = src
+
+ifdef ENABLE_TESTS
+DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/download.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+# -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# vim: set ts=2 et sw=2 tw=80:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
+
+<bindings id="downloadBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+
+  <binding id="download"
+           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <resources>
+      <stylesheet src="chrome://browser/skin/downloads/downloads.css"/>
+    </resources>
+    <content orient="horizontal">
+      <xul:hbox class="downloadInfo"
+                align="center"
+                flex="1"
+                onclick="DownloadsView.onDownloadClick(event);">
+        <xul:vbox pack="center">
+          <xul:image class="downloadTypeIcon"
+                     validate="always"
+                     xbl:inherits="src=image"/>
+          <xul:image class="downloadTypeIcon blockedIcon"/>
+        </xul:vbox>
+        <xul:vbox pack="center"
+                  flex="1">
+          <xul:description class="downloadTarget"
+                           crop="center"
+                           xbl:inherits="value=target,tooltiptext=target"/>
+          <xul:progressmeter anonid="progressmeter"
+                             class="downloadProgress"
+                             min="0"
+                             max="100"
+                             xbl:inherits="mode=progressmode,value=progress"/>
+          <xul:description class="downloadDetails"
+                           crop="end"
+                           xbl:inherits="value=status,tooltiptext=statusTip"/>
+        </xul:vbox>
+      </xul:hbox>
+      <xul:hbox class="downloadButtonContainer"
+                align="center">
+        <xul:button class="downloadButton downloadCancel"
+                    tooltiptext="&cmd.cancel.label;"
+                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
+        <xul:button class="downloadButton downloadRetry"
+                    tooltiptext="&cmd.retry.label;"
+                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
+        <xul:button class="downloadButton downloadShow"
+                    tooltiptext="&cmd.show.label;"
+                    oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_show');"/>
+      </xul:hbox>
+    </content>
+  </binding>
+</bindings>
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/downloads.css
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*** Download items ***/
+
+richlistitem[type="download"] {
+  -moz-binding: url('chrome://browser/content/downloads/download.xml#download');
+}
+
+richlistitem[type="download"]:not([selected]) button {
+  /* Only focus buttons in the selected item. */
+  -moz-user-focus: none;
+}
+
+/*** Visibility of controls inside download items ***/
+
+.download-state:-moz-any(     [state="6"], /* Blocked (parental) */
+                              [state="8"], /* Blocked (dirty)    */
+                              [state="9"]) /* Blocked (policy)   */
+                                           .downloadTypeIcon:not(.blockedIcon),
+
+.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
+                              [state="8"], /* Blocked (dirty)    */
+                              [state="9"]) /* Blocked (policy)   */)
+                                           .downloadTypeIcon.blockedIcon,
+
+.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
+                              [state="5"], /* Starting (queued)  */
+                              [state="0"], /* Downloading        */
+                              [state="4"], /* Paused             */
+                              [state="7"]) /* Scanning           */)
+                                           .downloadProgress,
+
+.download-state:not(          [state="0"]  /* Downloading        */)
+                                           .downloadPauseMenuItem,
+
+.download-state:not(          [state="4"]  /* Paused             */)
+                                           .downloadResumeMenuItem,
+
+.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
+                              [state="5"], /* Starting (queued)  */
+                              [state="0"], /* Downloading        */
+                              [state="4"]) /* Paused             */)
+                                           .downloadCancel,
+
+.download-state:not(:-moz-any([state="2"], /* Failed             */
+                              [state="4"]) /* Paused             */)
+                                           .downloadCancelMenuItem,
+
+.download-state:not(:-moz-any([state="1"], /* Finished           */
+                              [state="3"], /* Canceled           */
+                              [state="6"], /* Blocked (parental) */
+                              [state="8"], /* Blocked (dirty)    */
+                              [state="9"]) /* Blocked (policy)   */)
+                                           .downloadRemoveFromListMenuItem,
+
+.download-state:not(:-moz-any([state="2"], /* Failed             */
+                              [state="3"]) /* Canceled           */)
+                                           .downloadRetry,
+
+.download-state:not(          [state="1"]  /* Finished           */)
+                                           .downloadShow,
+
+.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
+                              [state="5"], /* Starting (queued)  */
+                              [state="0"], /* Downloading        */
+                              [state="4"]) /* Paused             */)
+                                           .downloadShowMenuItem,
+
+.download-state[state="7"]                 .downloadCommandsSeparator
+
+{
+  display: none;
+}
+
+/*** Visibility of controls inside the downloads indicator ***/
+
+#downloads-indicator:-moz-any([progress],
+                              [counter],
+                              [paused])    #downloads-indicator-icon,
+
+#downloads-indicator:not(:-moz-any([progress],
+                                   [counter],
+                                   [paused]))
+                                           #downloads-indicator-progress-area
+
+{
+  visibility: hidden;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/downloads.js
@@ -0,0 +1,1201 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Handles the Downloads panel user interface for each browser window.
+ *
+ * This file includes the following constructors and global objects:
+ *
+ * DownloadsPanel
+ * Main entry point for the downloads panel interface.
+ *
+ * DownloadsOverlayLoader
+ * Allows loading the downloads panel and the status indicator interfaces on
+ * demand, to improve startup performance.
+ *
+ * DownloadsView
+ * Builds and updates the downloads list widget, responding to changes in the
+ * download state and real-time data.  In addition, handles part of the user
+ * interaction events raised by the downloads list widget.
+ *
+ * DownloadsViewItem
+ * Builds and updates a single item in the downloads list widget, responding to
+ * changes in the download state and real-time data.
+ *
+ * DownloadsViewController
+ * Handles part of the user interaction events raised by the downloads list
+ * widget, in particular the "commands" that apply to multiple items, and
+ * dispatches the commands that apply to individual items.
+ *
+ * DownloadsViewItemController
+ * Handles all the user interaction events, in particular the "commands",
+ * related to a single item in the downloads list widgets.
+ */
+
+"use strict";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
+                                  "resource://gre/modules/DownloadUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsPanel
+
+/**
+ * Main entry point for the downloads panel interface.
+ */
+const DownloadsPanel = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Initialization and termination
+
+  /**
+   * State of the downloads panel, based on one of the kPanel constants.
+   */
+  _panelState: 0,
+
+  /** Download data has not been loaded. */
+  get kPanelUninitialized() 0,
+  /** Download data is loading, but the user interface is invisible. */
+  get kPanelHidden() 1,
+  /** The panel will be shown as soon as possible. */
+  get kPanelShowing() 2,
+  /** The panel is open, though download data might still be loading. */
+  get kPanelShown() 3,
+
+  /**
+   * Location of the panel overlay.
+   */
+  get kDownloadsOverlay()
+      "chrome://browser/content/downloads/downloadsOverlay.xul",
+
+  /**
+   * Starts loading the download data in background, without opening the panel.
+   * Use showPanel instead to load the data and open the panel at the same time.
+   *
+   * @param aCallback
+   *        Called when initialization is complete.
+   */
+  initialize: function DP_initialize(aCallback)
+  {
+    if (this._panelState != this.kPanelUninitialized) {
+      DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
+                                                 aCallback);
+      return;
+    }
+    this._panelState = this.kPanelHidden;
+
+    window.addEventListener("unload", this.onWindowUnload, false);
+
+    // Ensure that the Download Manager service is running.  This resumes
+    // active downloads if required.  If there are downloads to be shown in the
+    // panel, starting the service will make us load their data asynchronously.
+    Services.downloads;
+
+    // Now that data loading has eventually started, load the required XUL
+    // elements and initialize our views.
+    DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
+                                               function DP_I_callback() {
+      DownloadsViewController.initialize();
+      DownloadsCommon.data.addView(DownloadsView);
+      aCallback();
+    });
+  },
+
+  /**
+   * Closes the downloads panel and frees the internal resources related to the
+   * downloads.  The downloads panel can be reopened later, even after this
+   * function has been called.
+   */
+  terminate: function DP_terminate()
+  {
+    if (this._panelState == this.kPanelUninitialized) {
+      return;
+    }
+
+    window.removeEventListener("unload", this.onWindowUnload, false);
+
+    // Ensure that the panel is closed before shutting down.
+    this.hidePanel();
+
+    DownloadsViewController.terminate();
+    DownloadsCommon.data.removeView(DownloadsView);
+
+    this._panelState = this.kPanelUninitialized;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Panel interface
+
+  /**
+   * Main panel element in the browser window.
+   */
+  get panel()
+  {
+    delete this.panel;
+    return this.panel = document.getElementById("downloadsPanel");
+  },
+
+  /**
+   * Starts opening the downloads panel interface, anchored to the downloads
+   * button of the browser window.  The list of downloads to display is
+   * initialized the first time this method is called, and the panel is shown
+   * only when data is ready.
+   */
+  showPanel: function DP_showPanel()
+  {
+    if (this.isPanelShowing) {
+      this._focusPanel();
+      return;
+    }
+
+    this.initialize(function DP_SP_callback() {
+      // Delay displaying the panel because this function will sometimes be
+      // called while another window is closing (like the window for selecting
+      // whether to save or open the file), and that would cause the panel to
+      // close immediately.
+      setTimeout(function () DownloadsPanel._openPopupIfDataReady(), 0);
+    }.bind(this));
+
+    this._panelState = this.kPanelShowing;
+  },
+
+  /**
+   * Hides the downloads panel, if visible, but keeps the internal state so that
+   * the panel can be reopened quickly if required.
+   */
+  hidePanel: function DP_hidePanel()
+  {
+    if (!this.isPanelShowing) {
+      return;
+    }
+
+    this.panel.hidePopup();
+
+    // Ensure that we allow the panel to be reopened.  Note that, if the popup
+    // was open, then the onPopupHidden event handler has already updated the
+    // current state, otherwise we must update the state ourselves.
+    this._panelState = this.kPanelHidden;
+  },
+
+  /**
+   * Indicates whether the panel is shown or will be shown.
+   */
+  get isPanelShowing()
+  {
+    return this._panelState == this.kPanelShowing ||
+           this._panelState == this.kPanelShown;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsView
+
+  /**
+   * Called after data loading finished.
+   */
+  onViewLoadCompleted: function DP_onViewLoadCompleted()
+  {
+    this._openPopupIfDataReady();
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// User interface event functions
+
+  onWindowUnload: function DP_onWindowUnload()
+  {
+    // This function is registered as an event listener, we can't use "this".
+    DownloadsPanel.terminate();
+  },
+
+  onPopupShown: function DP_onPopupShown(aEvent)
+  {
+    // Ignore events raised by nested popups.
+    if (aEvent.target != aEvent.currentTarget) {
+      return;
+    }
+
+    // Since at most one popup is open at any given time, we can set globally.
+    DownloadsCommon.indicatorData.attentionSuppressed = true;
+
+    // Ensure that an item is selected when the panel is focused.
+    if (DownloadsView.richListBox.itemCount > 0 &&
+        !DownloadsView.richListBox.selectedItem) {
+      DownloadsView.richListBox.selectedIndex = 0;
+    }
+
+    this._focusPanel();
+  },
+
+  onPopupHidden: function DP_onPopupHidden(aEvent)
+  {
+    // Ignore events raised by nested popups.
+    if (aEvent.target != aEvent.currentTarget) {
+      return;
+    }
+
+    // Since at most one popup is open at any given time, we can set globally.
+    DownloadsCommon.indicatorData.attentionSuppressed = false;
+
+    // Allow the anchor to be hidden.
+    DownloadsButton.releaseAnchor();
+
+    // Allow the panel to be reopened.
+    this._panelState = this.kPanelHidden;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Related operations
+
+  /**
+   * Shows or focuses the user interface dedicated to downloads history.
+   */
+  showDownloadsHistory: function DP_showDownloadsHistory()
+  {
+    // Hide the panel before invoking the Library window, otherwise focus will
+    // return to the browser window when the panel closes automatically.
+    this.hidePanel();
+
+    // Open the Library window and select the Downloads query.
+    PlacesCommandHook.showPlacesOrganizer("Downloads");
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Internal functions
+
+  /**
+   * Move focus to the main element in the downloads panel, unless another
+   * element in the panel is already focused.
+   */
+  _focusPanel: function DP_focusPanel()
+  {
+    // We may be invoked while the panel is still waiting to be shown.
+    if (this._panelState != this.kPanelShown) {
+      return;
+    }
+
+    let element = document.commandDispatcher.focusedElement;
+    while (element && element != this.panel) {
+      element = element.parentNode;
+    }
+    if (!element) {
+      DownloadsView.richListBox.focus();
+    }
+  },
+
+  /**
+   * Opens the downloads panel when data is ready to be displayed.
+   */
+  _openPopupIfDataReady: function DP_openPopupIfDataReady()
+  {
+    // We don't want to open the popup if we already displayed it, or if we are
+    // still loading data.
+    if (this._panelState != this.kPanelShowing || DownloadsView.loading) {
+      return;
+    }
+
+    this._panelState = this.kPanelShown;
+
+    // Make sure that clicking outside the popup cannot reopen it accidentally.
+    this.panel.popupBoxObject.setConsumeRollupEvent(Ci.nsIPopupBoxObject
+                                                      .ROLLUP_CONSUME);
+
+    // Ensure the anchor is visible.  If that is not possible, show the panel
+    // anchored to the top area of the window, near the default anchor position.
+    DownloadsButton.getAnchor(function DP_OPIDR_callback(aAnchor) {
+      if (aAnchor) {
+        this.panel.openPopup(aAnchor, "bottomcenter topright", 0, 0, false,
+                             null);
+      } else {
+        this.panel.openPopup(document.getElementById("TabsToolbar"),
+                             "after_end", 0, 0, false, null);
+      }
+    }.bind(this));
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsOverlayLoader
+
+/**
+ * Allows loading the downloads panel and the status indicator interfaces on
+ * demand, to improve startup performance.
+ */
+const DownloadsOverlayLoader = {
+  /**
+   * We cannot load two overlays at the same time, thus we use a queue of
+   * pending load requests.
+   */
+  _loadRequests: [],
+
+  /**
+   * True while we are waiting for an overlay to be loaded.
+   */
+  _overlayLoading: false,
+
+  /**
+   * This object has a key for each overlay URI that is already loaded.
+   */
+  _loadedOverlays: {},
+
+  /**
+   * Loads the specified overlay and invokes the given callback when finished.
+   *
+   * @param aOverlay
+   *        String containing the URI of the overlay to load in the current
+   *        window.  If this overlay has already been loaded using this
+   *        function, then the overlay is not loaded again. 
+   * @param aCallback
+   *        Invoked when loading is completed.  If the overlay is already
+   *        loaded, the function is called immediately.
+   */
+  ensureOverlayLoaded: function DOL_ensureOverlayLoaded(aOverlay, aCallback)
+  {
+    // The overlay is already loaded, invoke the callback immediately.
+    if (aOverlay in this._loadedOverlays) {
+      aCallback();
+      return;
+    }
+
+    // The callback will be invoked when loading is finished.
+    this._loadRequests.push({ overlay: aOverlay, callback: aCallback });
+    if (this._overlayLoading) {
+      return;
+    }
+
+    function DOL_EOL_loadCallback() {
+      this._overlayLoading = false;
+      this._loadedOverlays[aOverlay] = true;
+
+      // Loading the overlay causes all the persisted XUL attributes to be
+      // reapplied, including "iconsize" on the toolbars.  Until bug 640158 is
+      // fixed, we must recalculate the correct "iconsize" attributes manually.
+      retrieveToolbarIconsizesFromTheme();
+
+      this.processPendingRequests();
+    }
+
+    this._overlayLoading = true;
+    document.loadOverlay(aOverlay, DOL_EOL_loadCallback.bind(this));
+  },
+
+  /**
+   * Re-processes all the currently pending requests, invoking the callbacks
+   * and/or loading more overlays as needed.  In most cases, there will be a
+   * single request for one overlay, that will be processed immediately.
+   */
+  processPendingRequests: function DOL_processPendingRequests()
+  {
+    // Re-process all the currently pending requests, yet allow more requests
+    // to be appended at the end of the array if we're not ready for them.
+    let currentLength = this._loadRequests.length;
+    for (let i = 0; i < currentLength; i++) {
+      let request = this._loadRequests.shift();
+
+      // We must call ensureOverlayLoaded again for each request, to check if
+      // the associated callback can be invoked now, or if we must still wait
+      // for the associated overlay to load.
+      this.ensureOverlayLoaded(request.overlay, request.callback);
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsView
+
+/**
+ * Builds and updates the downloads list widget, responding to changes in the
+ * download state and real-time data.  In addition, handles part of the user
+ * interaction events raised by the downloads list widget.
+ */
+const DownloadsView = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Functions handling download items in the list
+
+  /**
+   * Indicates whether we are still loading downloads data asynchronously.
+   */
+  loading: false,
+
+  /**
+   * Object containing all the available DownloadsViewItem objects, indexed by
+   * their numeric download identifier.
+   */
+  _viewItems: {},
+
+  /**
+   * Called when the number of items in the list changes.
+   */
+  _itemCountChanged: function DV_itemCountChanged()
+  {
+    if (Object.keys(this._viewItems).length > 0) {
+      DownloadsPanel.panel.setAttribute("hasdownloads", "true");
+    } else {
+      DownloadsPanel.panel.removeAttribute("hasdownloads");
+    }
+  },
+
+  /**
+   * Element corresponding to the list of downloads.
+   */
+  get richListBox()
+  {
+    delete this.richListBox;
+    return this.richListBox = document.getElementById("downloadsListBox");
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsData
+
+  /**
+   * Called before multiple downloads are about to be loaded.
+   */
+  onDataLoadStarting: function DV_onDataLoadStarting()
+  {
+    this.loading = true;
+  },
+
+  /**
+   * Called after data loading finished.
+   */
+  onDataLoadCompleted: function DV_onDataLoadCompleted()
+  {
+    this.loading = false;
+
+    // Notify the panel that all the initially available downloads have been
+    // loaded.  This ensures that the interface is visible, if still required.
+    DownloadsPanel.onViewLoadCompleted();
+  },
+
+  /**
+   * Called when the downloads database becomes unavailable (for example,
+   * entering Private Browsing Mode).  References to existing data should be
+   * discarded.
+   */
+  onDataInvalidated: function DV_onDataInvalidated()
+  {
+    DownloadsPanel.terminate();
+
+    // Clear the list by replacing with a shallow copy.
+    let emptyView = this.richListBox.cloneNode(false);
+    this.richListBox.parentNode.replaceChild(emptyView, this.richListBox);
+    this.richListBox = emptyView;
+    this._viewItems = {};
+  },
+
+  /**
+   * Called when a new download data item is available, either during the
+   * asynchronous data load or when a new download is started.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that was just added.
+   * @param aNewest
+   *        When true, indicates that this item is the most recent and should be
+   *        added in the topmost position.  This happens when a new download is
+   *        started.  When false, indicates that the item is the least recent
+   *        and should be appended.  The latter generally happens during the
+   *        asynchronous data load.
+   */
+  onDataItemAdded: function DV_onDataItemAdded(aDataItem, aNewest)
+  {
+    // Make the item and add it in the appropriate place in the list.
+    let element = document.createElement("richlistitem");
+    let viewItem = new DownloadsViewItem(aDataItem, element);
+    this._viewItems[aDataItem.downloadId] = viewItem;
+    if (aNewest) {
+      this.richListBox.insertBefore(element, this.richListBox.firstChild);
+    } else {
+      this.richListBox.appendChild(element);
+    }
+
+    this._itemCountChanged();
+  },
+
+  /**
+   * Called when a data item is removed.  Ensures that the widget associated
+   * with the view item is removed from the user interface.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that is being removed.
+   */
+  onDataItemRemoved: function DV_onDataItemRemoved(aDataItem)
+  {
+    let element = this.getViewItem(aDataItem)._element;
+    let previousSelectedIndex = this.richListBox.selectedIndex;
+    this.richListBox.removeChild(element);
+    this.richListBox.selectedIndex = Math.min(previousSelectedIndex,
+                                              this.richListBox.itemCount - 1);
+    delete this._viewItems[aDataItem.downloadId];
+
+    this._itemCountChanged();
+  },
+
+  /**
+   * Returns the view item associated with the provided data item for this view.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object for which the view item is requested.
+   *
+   * @return Object that can be used to notify item status events.
+   */
+  getViewItem: function DV_getViewItem(aDataItem)
+  {
+    return this._viewItems[aDataItem.downloadId];
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// User interface event functions
+
+  /**
+   * Helper function to do commands on a specific download item.
+   *
+   * @param aEvent
+   *        Event object for the event being handled.  If the event target is
+   *        not a richlistitem that represents a download, this function will
+   *        walk up the parent nodes until it finds a DOM node that is.
+   * @param aCommand
+   *        The command to be performed.
+   */
+  onDownloadCommand: function DV_onDownloadCommand(aEvent, aCommand)
+  {
+    let target = aEvent.target;
+    while (target.nodeName != "richlistitem") {
+      target = target.parentNode;
+    }
+    new DownloadsViewItemController(target).doCommand(aCommand);
+  },
+
+  onDownloadClick: function DV_onDownloadClick(aEvent)
+  {
+    // Handle primary clicks only.
+    if (aEvent.button == 0) {
+      goDoCommand("downloadsCmd_open");
+    }
+  },
+
+  onDownloadKeyPress: function DV_onDownloadKeyPress(aEvent)
+  {
+    // Handle unmodified keys only.
+    if (aEvent.altKey || aEvent.ctrlKey || aEvent.shiftKey || aEvent.metaKey) {
+      return;
+    }
+
+    // Pressing the key on buttons should not invoke the action because the
+    // event has already been handled by the button itself.
+    if (aEvent.originalTarget.hasAttribute("command") ||
+        aEvent.originalTarget.hasAttribute("oncommand")) {
+      return;
+    }
+
+    if (aEvent.charCode == " ".charCodeAt(0)) {
+      goDoCommand("downloadsCmd_pauseResume");
+      return;
+    }
+
+    switch (aEvent.keyCode) {
+      case KeyEvent.DOM_VK_ENTER:
+      case KeyEvent.DOM_VK_RETURN:
+        goDoCommand("downloadsCmd_doDefault");
+        break;
+    }
+  },
+
+  onDownloadContextMenu: function DV_onDownloadContextMenu(aEvent)
+  {
+    let element = this.richListBox.selectedItem;
+    if (!element) {
+      return;
+    }
+
+    DownloadsViewController.updateCommands();
+
+    // Set the state attribute so that only the appropriate items are displayed.
+    let contextMenu = document.getElementById("downloadsContextMenu");
+    contextMenu.setAttribute("state", element.getAttribute("state"));
+  },
+
+  onDownloadDragStart: function DV_onDownloadDragStart(aEvent)
+  {
+    let element = this.richListBox.selectedItem;
+    if (!element) {
+      return;
+    }
+
+    let controller = new DownloadsViewItemController(element);
+    let localFile = controller.dataItem.localFile;
+    if (!localFile.exists()) {
+      return;
+    }
+
+    let dataTransfer = aEvent.dataTransfer;
+    dataTransfer.mozSetDataAt("application/x-moz-file", localFile, 0);
+    dataTransfer.effectAllowed = "copyMove";
+    dataTransfer.addElement(element);
+
+    aEvent.stopPropagation();
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsViewItem
+
+/**
+ * Builds and updates a single item in the downloads list widget, responding to
+ * changes in the download state and real-time data.
+ *
+ * @param aDataItem
+ *        DownloadsDataItem to be associated with the view item.
+ * @param aElement
+ *        XUL element corresponding to the single download item in the view.
+ */
+function DownloadsViewItem(aDataItem, aElement)
+{
+  this._element = aElement;
+  this.dataItem = aDataItem;
+
+  this.wasDone = this.dataItem.done;
+  this.wasInProgress = this.dataItem.inProgress;
+  this.lastEstimatedSecondsLeft = Infinity;
+
+  // Set the URI that represents the correct icon for the target file.  As soon
+  // as bug 239948 comment 12 is handled, the "file" property will be always a
+  // file URL rather than a file name.  At that point we should remove the "//"
+  // (double slash) from the icon URI specification (see test_moz_icon_uri.js).
+  this.image = "moz-icon://" + this.dataItem.file + "?size=32";
+
+  let attributes = {
+    "type": "download",
+    "class": "download-state",
+    "id": "downloadsItem_" + this.dataItem.downloadId,
+    "downloadId": this.dataItem.downloadId,
+    "state": this.dataItem.state,
+    "progress": this.dataItem.inProgress ? this.dataItem.percentComplete : 100,
+    "target": this.dataItem.target,
+    "image": this.image
+  };
+
+  for (let attributeName in attributes) {
+    this._element.setAttribute(attributeName, attributes[attributeName]);
+  }
+
+  // Initialize more complex attributes.
+  this._updateProgress();
+  this._updateStatusLine();
+}
+
+DownloadsViewItem.prototype = {
+  /**
+   * The DownloadDataItem associated with this view item.
+   */
+  dataItem: null,
+
+  /**
+   * The XUL element corresponding to the associated richlistbox item.
+   */
+  _element: null,
+
+  /**
+   * The inner XUL element for the progress bar, or null if not available.
+   */
+  _progressElement: null,
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsData
+
+  /**
+   * Called when the download state might have changed.  Sometimes the state of
+   * the download might be the same as before, if the data layer received
+   * multiple events for the same download.
+   */
+  onStateChange: function DVI_onStateChange()
+  {
+    // If a download just finished successfully, it means that the target file
+    // now exists and we can extract its specific icon.  To ensure that the icon
+    // is reloaded, we must change the URI used by the XUL image element, for
+    // example by adding a query parameter.  Since this URI has a "moz-icon"
+    // scheme, this only works if we add one of the parameters explicitly
+    // supported by the nsIMozIconURI interface.
+    if (!this.wasDone && this.dataItem.openable) {
+      this._element.setAttribute("image", this.image + "&state=normal");
+    }
+
+    // Update the end time using the current time if required.
+    if (this.wasInProgress && !this.dataItem.inProgress) {
+      this.endTime = Date.now();
+    }
+
+    this.wasDone = this.dataItem.done;
+    this.wasInProgress = this.dataItem.inProgress;
+
+    // Update the user interface after switching states.
+    this._element.setAttribute("state", this.dataItem.state);
+    this._updateProgress();
+    this._updateStatusLine();
+  },
+
+  /**
+   * Called when the download progress has changed.
+   */
+  onProgressChange: function DVI_onProgressChange() {
+    this._updateProgress();
+    this._updateStatusLine();
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Functions for updating the user interface
+
+  /**
+   * Updates the progress bar.
+   */
+  _updateProgress: function DVI_updateProgress() {
+    if (this.dataItem.starting) {
+      // Before the download starts, the progress meter has its initial value.
+      this._element.setAttribute("progressmode", "normal");
+      this._element.setAttribute("progress", "0");
+    } else if (this.dataItem.state == Ci.nsIDownloadManager.DOWNLOAD_SCANNING ||
+               this.dataItem.percentComplete == -1) {
+      // We might not know the progress of a running download, and we don't know
+      // the remaining time during the malware scanning phase.
+      this._element.setAttribute("progressmode", "undetermined");
+    } else {
+      // This is a running download of which we know the progress.
+      this._element.setAttribute("progressmode", "normal");
+      this._element.setAttribute("progress", this.dataItem.percentComplete);
+    }
+
+    // Find the progress element as soon as the download binding is accessible.
+    if (!this._progressElement) {
+      this._progressElement =
+           document.getAnonymousElementByAttribute(this._element, "anonid",
+                                                   "progressmeter");
+    }
+
+    // Dispatch the ValueChange event for accessibility, if possible.
+    if (this._progressElement) {
+      let event = document.createEvent("Events");
+      event.initEvent("ValueChange", true, true);
+      this._progressElement.dispatchEvent(event);
+    }
+  },
+
+  /**
+   * Updates the main status line, including bytes transferred, bytes total,
+   * download rate, and time remaining.
+   */
+  _updateStatusLine: function DVI_updateStatusLine() {
+    const nsIDM = Ci.nsIDownloadManager;
+
+    let status = "";
+    let statusTip = "";
+
+    if (this.dataItem.paused) {
+      let transfer = DownloadUtils.getTransferTotal(this.dataItem.currBytes,
+                                                    this.dataItem.maxBytes);
+
+      // We use the same XUL label to display both the state and the amount
+      // transferred, for example "Paused -  1.1 MB".
+      status = DownloadsCommon.strings.statusSeparatorBeforeNumber(
+                                            DownloadsCommon.strings.statePaused,
+                                            transfer);
+    } else if (this.dataItem.state == nsIDM.DOWNLOAD_DOWNLOADING) {
+      let newEstimatedSecondsLeft;
+      [status, newEstimatedSecondsLeft] =
+        DownloadUtils.getDownloadStatus(this.dataItem.currBytes,
+                                        this.dataItem.maxBytes,
+                                        this.dataItem.speed,
+                                        this.lastEstimatedSecondsLeft);
+      this.lastEstimatedSecondsLeft = newEstimatedSecondsLeft;
+    } else if (this.dataItem.starting) {
+      status = DownloadsCommon.strings.stateStarting;
+    } else if (this.dataItem.state == nsIDM.DOWNLOAD_SCANNING) {
+      status = DownloadsCommon.strings.stateScanning;
+    } else if (!this.dataItem.inProgress) {
+      let stateLabel = function () {
+        let s = DownloadsCommon.strings;
+        switch (this.dataItem.state) {
+          case nsIDM.DOWNLOAD_FAILED:           return s.stateFailed;
+          case nsIDM.DOWNLOAD_CANCELED:         return s.stateCanceled;
+          case nsIDM.DOWNLOAD_BLOCKED_PARENTAL: return s.stateBlockedParentalControls;
+          case nsIDM.DOWNLOAD_BLOCKED_POLICY:   return s.stateBlockedPolicy;
+          case nsIDM.DOWNLOAD_DIRTY:            return s.stateDirty;
+          case nsIDM.DOWNLOAD_FINISHED:         return this._fileSizeText;
+        }
+        return null;
+      }.apply(this);
+
+      let [displayHost, fullHost] =
+        DownloadUtils.getURIHost(this.dataItem.referrer || this.dataItem.uri);
+
+      let end = new Date(this.dataItem.endTime);
+      let [displayDate, fullDate] = DownloadUtils.getReadableDates(end);
+
+      // We use the same XUL label to display the state, the host name, and the
+      // end time, for example "Canceled - 222.net - 11:15" or "1.1 MB -
+      // website2.com - Yesterday".  We show the full host and the complete date
+      // in the tooltip.
+      let firstPart = DownloadsCommon.strings.statusSeparator(stateLabel,
+                                                              displayHost);
+      status = DownloadsCommon.strings.statusSeparator(firstPart, displayDate);
+      statusTip = DownloadsCommon.strings.statusSeparator(fullHost, fullDate);
+    }
+
+    this._element.setAttribute("status", status);
+    this._element.setAttribute("statusTip", statusTip || status);
+  },
+
+  /**
+   * Localized string representing the total size of completed downloads, for
+   * example "1.5 MB" or "Unknown size".
+   */
+  get _fileSizeText()
+  {
+    // Display the file size, but show "Unknown" for negative sizes.
+    let fileSize = this.dataItem.maxBytes;
+    if (fileSize < 0) {
+      return DownloadsCommon.strings.sizeUnknown;
+    }
+    let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
+    return DownloadsCommon.strings.sizeWithUnits(size, unit);
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsViewController
+
+/**
+ * Handles part of the user interaction events raised by the downloads list
+ * widget, in particular the "commands" that apply to multiple items, and
+ * dispatches the commands that apply to individual items.
+ */
+const DownloadsViewController = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Initialization and termination
+
+  initialize: function DVC_initialize()
+  {
+    window.controllers.insertControllerAt(0, this);
+  },
+
+  terminate: function DVC_terminate()
+  {
+    window.controllers.removeController(this);
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIController
+
+  supportsCommand: function DVC_supportsCommand(aCommand)
+  {
+    // Firstly, determine if this is a command that we can handle.
+    if (!(aCommand in this.commands) &&
+        !(aCommand in DownloadsViewItemController.prototype.commands)) {
+      return false;
+    }
+    // Secondly, determine if focus is on a control in the downloads list.
+    let element = document.commandDispatcher.focusedElement;
+    while (element && element != DownloadsView.richListBox) {
+      element = element.parentNode;
+    }
+    // We should handle the command only if the downloads list is among the
+    // ancestors of the focused element.
+    return !!element;
+  },
+
+  isCommandEnabled: function DVC_isCommandEnabled(aCommand)
+  {
+    // Handle commands that are not selection-specific.
+    if (aCommand == "downloadsCmd_clearList") {
+      return Services.downloads.canCleanUp;
+    }
+
+    // Other commands are selection-specific.
+    let element = DownloadsView.richListBox.selectedItem;
+    return element &&
+           new DownloadsViewItemController(element).isCommandEnabled(aCommand);
+  },
+
+  doCommand: function DVC_doCommand(aCommand)
+  {
+    // If this command is not selection-specific, execute it.
+    if (aCommand in this.commands) {
+      this.commands[aCommand].apply(this);
+      return;
+    }
+
+    // Other commands are selection-specific.
+    let element = DownloadsView.richListBox.selectedItem;
+    if (element) {
+      // The doCommand function also checks if the command is enabled.
+      new DownloadsViewItemController(element).doCommand(aCommand);
+    }
+  },
+
+  onEvent: function () { },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Other functions
+
+  updateCommands: function DVC_updateCommands()
+  {
+    Object.keys(this.commands).forEach(goUpdateCommand);
+    Object.keys(DownloadsViewItemController.prototype.commands)
+          .forEach(goUpdateCommand);
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Selection-independent commands
+
+  /**
+   * This object contains one key for each command that operates regardless of
+   * the currently selected item in the list.
+   */
+  commands: {
+    downloadsCmd_clearList: function DVC_downloadsCmd_clearList()
+    {
+      Services.downloads.cleanUp();
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsViewItemController
+
+/**
+ * Handles all the user interaction events, in particular the "commands",
+ * related to a single item in the downloads list widgets.
+ */
+function DownloadsViewItemController(aElement) {
+  let downloadId = aElement.getAttribute("downloadId");
+  this.dataItem = DownloadsCommon.data.dataItems[downloadId];
+}
+
+DownloadsViewItemController.prototype = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Constants
+
+  get kPrefBdmAlertOnExeOpen() "browser.download.manager.alertOnEXEOpen",
+  get kPrefBdmScanWhenDone() "browser.download.manager.scanWhenDone",
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Command dispatching
+
+  /**
+   * The DownloadDataItem controlled by this object.
+   */
+  dataItem: null,
+
+  isCommandEnabled: function DVIC_isCommandEnabled(aCommand)
+  {
+    switch (aCommand) {
+      case "downloadsCmd_open": {
+        return this.dataItem.openable && this.dataItem.localFile.exists();
+      }
+      case "downloadsCmd_show": {
+        return this.dataItem.localFile.exists();
+      }
+      case "downloadsCmd_pauseResume":
+        return this.dataItem.inProgress && this.dataItem.resumable;
+      case "downloadsCmd_retry":
+        return this.dataItem.canRetry;
+      case "downloadsCmd_openReferrer":
+        return !!this.dataItem.referrer;
+      case "cmd_delete":
+      case "downloadsCmd_cancel":
+      case "downloadsCmd_copyLocation":
+      case "downloadsCmd_doDefault":
+        return true;
+    }
+    return false;
+  },
+
+  doCommand: function DVIC_doCommand(aCommand)
+  {
+    if (this.isCommandEnabled(aCommand)) {
+      this.commands[aCommand].apply(this);
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Item commands
+
+  /**
+   * This object contains one key for each command that operates on this item.
+   *
+   * In commands, the "this" identifier points to the controller item.
+   */
+  commands: {
+    cmd_delete: function DVIC_cmd_delete()
+    {
+      this.commands.downloadsCmd_cancel.apply(this);
+
+      Services.downloads.removeDownload(this.dataItem.downloadId);
+    },
+
+    downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
+    {
+      if (this.dataItem.inProgress) {
+        Services.downloads.cancelDownload(this.dataItem.downloadId);
+
+        // It is possible that in some cases the Download Manager service
+        // doesn't delete the file from disk when canceling.  See bug 732924.
+        try {
+          let localFile = this.dataItem.localFile;
+          if (localFile.exists()) {
+            localFile.remove(false);
+          }
+        } catch (ex) { }
+      }
+    },
+
+    downloadsCmd_open: function DVIC_downloadsCmd_open()
+    {
+      // Confirm opening executable files if required.
+      let localFile = this.dataItem.localFile;
+      if (localFile.isExecutable()) {
+        let showAlert = true;
+        try {
+          showAlert = Services.prefs.getBoolPref(this.kPrefBdmAlertOnExeOpen);
+        } catch (ex) { }
+
+        // On Vista and above, we rely on native security prompting for
+        // downloaded content unless it's disabled.
+        if (DownloadsCommon.isWinVistaOrHigher) {
+          try {
+            if (Services.prefs.getBoolPref(this.kPrefBdmScanWhenDone)) {
+              showAlert = false;
+            }
+          } catch (ex) { }
+        }
+
+        if (showAlert) {
+          let name = this.dataItem.target;
+          let message =
+              DownloadsCommon.strings.fileExecutableSecurityWarning(name, name);
+          let title =
+              DownloadsCommon.strings.fileExecutableSecurityWarningTitle;
+          let dontAsk =
+              DownloadsCommon.strings.fileExecutableSecurityWarningDontAsk;
+
+          let checkbox = { value: false };
+          let open = Services.prompt.confirmCheck(window, title, message,
+                                                  dontAsk, checkbox);
+          if (!open) {
+            return;
+          }
+
+          Services.prefs.setBoolPref(this.kPrefBdmAlertOnExeOpen,
+                                     !checkbox.value);
+        }
+      }
+
+      // Actually open the file.
+      try {
+        let launched = false;
+        try {
+          let mimeInfo = this.dataItem.download.MIMEInfo;
+          if (mimeInfo.preferredAction == mimeInfo.useHelperApp) {
+            mimeInfo.launchWithFile(localFile);
+            launched = true;
+          }
+        } catch (ex) { }
+        if (!launched) {
+          localFile.launch();
+        }
+      } catch (ex) {
+        // If launch fails, try sending it through the system's external "file:"
+        // URL handler.
+        this._openExternal(localFile);
+      }
+    },
+
+    downloadsCmd_show: function DVIC_downloadsCmd_show()
+    {
+      let localFile = this.dataItem.localFile;
+
+      try {
+        // Show the directory containing the file and select the file.
+        localFile.reveal();
+      } catch (ex) {
+        // If reveal fails for some reason (e.g., it's not implemented on unix
+        // or the file doesn't exist), try using the parent if we have it.
+        let parent = localFile.parent.QueryInterface(Ci.nsILocalFile);
+        if (parent) {
+          try {
+            // Open the parent directory to show where the file should be.
+            parent.launch();
+          } catch (ex) {
+            // If launch also fails (probably because it's not implemented), let
+            // the OS handler try to open the parent.
+            this._openExternal(parent);
+          }
+        }
+      }
+    },
+
+    downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume()
+    {
+      if (this.dataItem.paused) {
+        Services.downloads.resumeDownload(this.dataItem.downloadId);
+      } else {
+        Services.downloads.pauseDownload(this.dataItem.downloadId);
+      }
+    },
+
+    downloadsCmd_retry: function DVIC_downloadsCmd_retry()
+    {
+      Services.downloads.retryDownload(this.dataItem.downloadId);
+    },
+
+    downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer()
+    {
+      openURL(this.dataItem.referrer);
+    },
+
+    downloadsCmd_copyLocation: function DVIC_downloadsCmd_copyLocation()
+    {
+      let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+                      .getService(Ci.nsIClipboardHelper);
+      clipboard.copyString(this.dataItem.uri);
+    },
+
+    downloadsCmd_doDefault: function DVIC_downloadsCmd_doDefault()
+    {
+      const nsIDM = Ci.nsIDownloadManager;
+
+      // Determine the default command for the current item.
+      let defaultCommand = function () {
+        switch (this.dataItem.state) {
+          case nsIDM.DOWNLOAD_NOTSTARTED:       return "downloadsCmd_cancel";
+          case nsIDM.DOWNLOAD_DOWNLOADING:      return "downloadsCmd_show";
+          case nsIDM.DOWNLOAD_FINISHED:         return "downloadsCmd_open";
+          case nsIDM.DOWNLOAD_FAILED:           return "downloadsCmd_retry";
+          case nsIDM.DOWNLOAD_CANCELED:         return "downloadsCmd_retry";
+          case nsIDM.DOWNLOAD_PAUSED:           return "downloadsCmd_pauseResume";
+          case nsIDM.DOWNLOAD_QUEUED:           return "downloadsCmd_cancel";
+          case nsIDM.DOWNLOAD_BLOCKED_PARENTAL: return "downloadsCmd_openReferrer";
+          case nsIDM.DOWNLOAD_SCANNING:         return "downloadsCmd_show";
+          case nsIDM.DOWNLOAD_DIRTY:            return "downloadsCmd_openReferrer";
+          case nsIDM.DOWNLOAD_BLOCKED_POLICY:   return "downloadsCmd_openReferrer";
+        }
+        return null;
+      }.apply(this);
+      // Invoke the command.
+      this.doCommand(defaultCommand);
+    }
+  },
+
+  /**
+   * Support function to open the specified nsIFile.
+   */
+  _openExternal: function DVIC_openExternal(aFile)
+  {
+    let protocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+                      .getService(Ci.nsIExternalProtocolService);
+    protocolSvc.loadUrl(makeFileURI(aFile));
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -0,0 +1,114 @@
+<?xml version="1.0"?>
+# -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# vim: set ts=2 et sw=2 tw=80:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
+<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
+
+<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+         id="downloadsOverlay">
+
+  <commandset>
+    <command id="downloadsCmd_doDefault"
+             oncommand="goDoCommand('downloadsCmd_doDefault')"/>
+    <command id="downloadsCmd_pauseResume"
+             oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
+    <command id="downloadsCmd_cancel"
+             oncommand="goDoCommand('downloadsCmd_cancel')"/>
+    <command id="downloadsCmd_open"
+             oncommand="goDoCommand('downloadsCmd_open')"/>
+    <command id="downloadsCmd_show"
+             oncommand="goDoCommand('downloadsCmd_show')"/>
+    <command id="downloadsCmd_retry"
+             oncommand="goDoCommand('downloadsCmd_retry')"/>
+    <command id="downloadsCmd_openReferrer"
+             oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
+    <command id="downloadsCmd_copyLocation"
+             oncommand="goDoCommand('downloadsCmd_copyLocation')"/>
+    <command id="downloadsCmd_clearList"
+             oncommand="goDoCommand('downloadsCmd_clearList')"/>
+  </commandset>
+
+  <popupset>
+    <!-- The panel has level="top" to ensure that it is never hidden by the
+         taskbar on Windows.  See bug 672365.  For accessibility to screen
+         readers, we use a label on the panel instead of the anchor because the
+         panel can also be displayed without an anchor. -->
+    <panel id="downloadsPanel"
+           aria-label="&downloads.title;"
+           role="group"
+           type="arrow"
+           orient="vertical"
+           level="top"
+           onpopupshown="DownloadsPanel.onPopupShown(event);"
+           onpopuphidden="DownloadsPanel.onPopupHidden(event);">
+      <!-- The following popup menu should be a child of the panel element,
+           otherwise flickering may occur when the cursor is moved over the area
+           of a disabled menu item that overlaps the panel.  See bug 492960. -->
+      <menupopup id="downloadsContextMenu"
+                 class="download-state">
+        <menuitem command="downloadsCmd_pauseResume"
+                  class="downloadPauseMenuItem"
+                  label="&cmd.pause.label;"
+                  accesskey="&cmd.pause.accesskey;"/>
+        <menuitem command="downloadsCmd_pauseResume"
+                  class="downloadResumeMenuItem"
+                  label="&cmd.resume.label;"
+                  accesskey="&cmd.resume.accesskey;"/>
+        <menuitem command="downloadsCmd_cancel"
+                  class="downloadCancelMenuItem"
+                  label="&cmd.cancel.label;"
+                  accesskey="&cmd.cancel.accesskey;"/>
+        <menuitem command="cmd_delete"
+                  class="downloadRemoveFromListMenuItem"
+                  label="&cmd.removeFromList.label;"
+                  accesskey="&cmd.removeFromList.accesskey;"/>
+        <menuitem command="downloadsCmd_show"
+                  class="downloadShowMenuItem"
+#ifdef XP_MACOSX
+                  label="&cmd.showMac.label;"
+                  accesskey="&cmd.showMac.accesskey;"
+#else
+                  label="&cmd.show.label;"
+                  accesskey="&cmd.show.accesskey;"
+#endif
+                  />
+
+        <menuseparator class="downloadCommandsSeparator"/>
+
+        <menuitem command="downloadsCmd_openReferrer"
+                  label="&cmd.goToDownloadPage.label;"
+                  accesskey="&cmd.goToDownloadPage.accesskey;"/>
+        <menuitem command="downloadsCmd_copyLocation"
+                  label="&cmd.copyDownloadLink.label;"
+                  accesskey="&cmd.copyDownloadLink.accesskey;"/>
+
+        <menuseparator/>
+
+        <menuitem command="downloadsCmd_clearList"
+                  label="&cmd.clearList.label;"
+                  accesskey="&cmd.clearList.accesskey;"/>
+      </menupopup>
+
+      <richlistbox id="downloadsListBox"
+                   class="plain"
+                   flex="1"
+                   context="downloadsContextMenu"
+                   onkeypress="DownloadsView.onDownloadKeyPress(event);"
+                   oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
+                   ondragstart="DownloadsView.onDownloadDragStart(event);"/>
+
+      <button id="downloadsHistory"
+              class="plain"
+              label="&downloadshistory.label;"
+              accesskey="&downloadshistory.accesskey;"
+              oncommand="DownloadsPanel.showDownloadsHistory();"/>
+    </panel>
+  </popupset>
+</overlay>
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/indicator.js
@@ -0,0 +1,591 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Handles the indicator that displays the progress of ongoing downloads, which
+ * is also used as the anchor for the downloads panel.
+ *
+ * This module includes the following constructors and global objects:
+ *
+ * DownloadsButton
+ * Main entry point for the downloads indicator.  Depending on how the toolbars
+ * have been customized, this object determines if we should show a fully
+ * functional indicator, a placeholder used during customization and in the
+ * customization palette, or a neutral view as a temporary anchor for the
+ * downloads panel.
+ *
+ * DownloadsIndicatorView
+ * Builds and updates the actual downloads status widget, responding to changes
+ * in the global status data, or provides a neutral view if the indicator is
+ * removed from the toolbars and only used as a temporary anchor.  In addition,
+ * handles the user interaction events raised by the widget.
+ */
+
+"use strict";
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsButton
+
+/**
+ * Main entry point for the downloads indicator.  Depending on how the toolbars
+ * have been customized, this object determines if we should show a fully
+ * functional indicator, a placeholder used during customization and in the
+ * customization palette, or a neutral view as a temporary anchor for the
+ * downloads panel.
+ */
+const DownloadsButton = {
+  /**
+   * Location of the indicator overlay.
+   */
+  get kIndicatorOverlay()
+      "chrome://browser/content/downloads/indicatorOverlay.xul",
+
+  /**
+   * Returns a reference to the downloads button position placeholder, or null
+   * if not available because it has been removed from the toolbars.
+   */
+  get _placeholder()
+  {
+    return document.getElementById("downloads-button");
+  },
+
+  /**
+   * This function is called synchronously at window initialization.  It only
+   * sets the visibility of user interface elements to avoid flickering.
+   *
+   * NOTE: To keep startup time to a minimum, this function should not perform
+   *       any expensive operations or input/output, and should not cause the
+   *       Download Manager service to start.
+   */
+  initializePlaceholder: function DB_initializePlaceholder()
+  {
+    // Exit now if the feature is disabled.  To improve startup time, we don't
+    // load the DownloadsCommon module yet, but check the preference directly.
+    if (gPrefService.getBoolPref("browser.download.useToolkitUI")) {
+      return;
+    }
+
+    // We must hide the placeholder used for toolbar customization, unless it
+    // has been removed from the toolbars and is now located in the palette.
+    let placeholder = this._placeholder;
+    if (placeholder) {
+      placeholder.collapsed = true;
+    }
+  },
+
+  /**
+   * This function is called asynchronously just after window initialization.
+   *
+   * NOTE: This function should limit the input/output it performs to improve
+   *       startup time, and in particular should not cause the Download Manager
+   *       service to start.
+   */
+  initializeIndicator: function DB_initializeIndicator()
+  {
+    this._update();
+  },
+
+  /**
+   * Indicates whether toolbar customization is in progress.
+   */
+  _customizing: false,
+
+  /**
+   * This function is called when toolbar customization starts.
+   *
+   * During customization, we never show the actual download progress indication
+   * or the event notifications, but we show a neutral placeholder.  The neutral
+   * placeholder is an ordinary button defined in the browser window that can be
+   * moved freely between the toolbars and the customization palette.
+   */
+  customizeStart: function DB_customizeStart()
+  {
+    // Hide the indicator and prevent it to be displayed as a temporary anchor
+    // during customization, even if requested using the getAnchor method.
+    this._customizing = true;
+    this._anchorRequested = false;
+
+    let indicator = DownloadsIndicatorView.indicator;
+    if (indicator) {
+      indicator.collapsed = true;
+    }
+
+    let placeholder = this._placeholder;
+    if (placeholder) {
+      placeholder.collapsed = false;
+    }
+  },
+
+  /**
+   * This function is called when toolbar customization ends.
+   */
+  customizeDone: function DB_customizeDone()
+  {
+    this._customizing = false;
+    this._update();
+  },
+
+  /**
+   * This function is called during initialization or when toolbar customization
+   * ends.  It determines if we should enable or disable the object that keeps
+   * the indicator updated, and ensures that the placeholder is hidden unless it
+   * has been moved to the customization palette.
+   *
+   * NOTE: This function is also called on startup, thus it should limit the
+   *       input/output it performs, and in particular should not cause the
+   *       Download Manager service to start.
+   */
+  _update: function DB_update() {
+    this._updatePositionInternal();
+
+    let placeholder = this._placeholder;
+    if (!DownloadsCommon.useToolkitUI) {
+      DownloadsIndicatorView.ensureInitialized();
+      if (placeholder) {
+        placeholder.collapsed = true;
+      }
+    } else {
+      DownloadsIndicatorView.ensureTerminated();
+    }
+  },
+
+  /**
+   * Determines the position where the indicator should appear, and moves its
+   * associated element to the new position.  This does not happen if the
+   * indicator is currently being used as the anchor for the panel, to ensure
+   * that the panel doesn't flicker because we move the DOM element to which
+   * it's anchored.
+   */
+  updatePosition: function DB_updatePosition()
+  {
+    if (!this._anchorRequested) {
+      this._updatePositionInternal();
+    }
+  },
+
+  /**
+   * Determines the position where the indicator should appear, and moves its
+   * associated element to the new position.
+   *
+   * @return Anchor element, or null if the indicator is not visible.
+   */
+  _updatePositionInternal: function DB_updatePositionInternal()
+  {
+    let indicator = DownloadsIndicatorView.indicator;
+    if (!indicator) {
+      // Exit now if the indicator overlay isn't loaded yet.
+      return null;
+    }
+
+    let placeholder = this._placeholder;
+
+    // Firstly, determine if we should always hide the indicator.
+    if (!placeholder && !this._anchorRequested &&
+        !DownloadsIndicatorView.hasDownloads) {
+      indicator.collapsed = true;
+      return null;
+    }
+    indicator.collapsed = false;
+
+    indicator.open = this._anchorRequested;
+
+    // Determine if we should display the indicator in a known position.
+    if (placeholder) {
+      placeholder.parentNode.insertBefore(indicator, placeholder);
+      // Determine if the placeholder is located on a visible toolbar.
+      if (isElementVisible(placeholder.parentNode)) {
+        return DownloadsIndicatorView.indicatorAnchor;
+      }
+    }
+
+    // If not customized, the indicator is normally in the navigation bar.
+    // Always place it in the default position, unless we need an anchor.
+    if (!this._anchorRequested) {
+      this._navBar.appendChild(indicator);
+      return null;
+    }
+
+    // Show the indicator temporarily in the navigation bar, if visible.
+    if (isElementVisible(this._navBar)) {
+      this._navBar.appendChild(indicator);
+      return DownloadsIndicatorView.indicatorAnchor;
+    }
+
+    // Show the indicator temporarily in the tab bar, if visible.
+    if (!this._tabsToolbar.collapsed) {
+      this._tabsToolbar.appendChild(indicator);
+      return DownloadsIndicatorView.indicatorAnchor;
+    }
+
+    // The temporary anchor cannot be shown.
+    return null;
+  },
+
+  /**
+   * Indicates whether we should try and show the indicator temporarily as an
+   * anchor for the panel, even if the indicator would be hidden by default.
+   */
+  _anchorRequested: false,
+
+  /**
+   * Ensures that there is an anchor available for the panel.
+   *
+   * @param aCallback
+   *        Called when the anchor is available, passing the element where the
+   *        panel should be anchored, or null if an anchor is not available (for
+   *        example because both the tab bar and the navigation bar are hidden).
+   */
+  getAnchor: function DB_getAnchor(aCallback)
+  {
+    // Do not allow anchoring the panel to the element while customizing.
+    if (this._customizing) {
+      aCallback(null);
+      return;
+    }
+
+    function DB_GA_callback() {
+      this._anchorRequested = true;
+      aCallback(this._updatePositionInternal());
+    }
+
+    DownloadsOverlayLoader.ensureOverlayLoaded(this.kIndicatorOverlay,
+                                               DB_GA_callback.bind(this));
+  },
+
+  /**
+   * Allows the temporary anchor to be hidden.
+   */
+  releaseAnchor: function DB_releaseAnchor()
+  {
+    this._anchorRequested = false;
+    this._updatePositionInternal();
+  },
+
+  get _tabsToolbar()
+  {
+    delete this._tabsToolbar;
+    return this._tabsToolbar = document.getElementById("TabsToolbar");
+  },
+
+  get _navBar()
+  {
+    delete this._navBar;
+    return this._navBar = document.getElementById("nav-bar");
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsIndicatorView
+
+/**
+ * Builds and updates the actual downloads status widget, responding to changes
+ * in the global status data, or provides a neutral view if the indicator is
+ * removed from the toolbars and only used as a temporary anchor.  In addition,
+ * handles the user interaction events raised by the widget.
+ */
+const DownloadsIndicatorView = {
+  /**
+   * True when the view is connected with the underlying downloads data.
+   */
+  _initialized: false,
+
+  /**
+   * True when the user interface elements required to display the indicator
+   * have finished loading in the browser window, and can be referenced.
+   */
+  _operational: false,
+
+  /**
+   * Prepares the downloads indicator to be displayed.
+   */
+  ensureInitialized: function DIV_ensureInitialized()
+  {
+    if (this._initialized) {
+      return;
+    }
+    this._initialized = true;
+
+    window.addEventListener("unload", this.onWindowUnload, false);
+    DownloadsCommon.indicatorData.addView(this);
+  },
+
+  /**
+   * Frees the internal resources related to the indicator.
+   */
+  ensureTerminated: function DIV_ensureTerminated()
+  {
+    if (!this._initialized) {
+      return;
+    }
+    this._initialized = false;
+
+    window.removeEventListener("unload", this.onWindowUnload, false);
+    DownloadsCommon.indicatorData.removeView(this);
+
+    // Reset the view properties, so that a neutral indicator is displayed if we
+    // are visible only temporarily as an anchor.
+    this.counter = "";
+    this.percentComplete = 0;
+    this.paused = false;
+    this.attention = false;
+  },
+
+  /**
+   * Ensures that the user interface elements required to display the indicator
+   * are loaded, then invokes the given callback.
+   */
+  _ensureOperational: function DIV_ensureOperational(aCallback)
+  {
+    if (this._operational) {
+      aCallback();
+      return;
+    }
+
+    function DIV_EO_callback() {
+      this._operational = true;
+
+      // If the view is initialized, we need to update the elements now that
+      // they are finally available in the document.
+      if (this._initialized) {
+        DownloadsCommon.indicatorData.refreshView(this);
+      }
+
+      aCallback();
+    }
+
+    DownloadsOverlayLoader.ensureOverlayLoaded(
+                                 DownloadsButton.kIndicatorOverlay,
+                                 DIV_EO_callback.bind(this));
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Direct control functions
+
+  /**
+   * Set while we are waiting for a notification to fade out.
+   */
+  _notificationTimeout: null,
+
+  /**
+   * If the status indicator is visible in its assigned position, shows for a
+   * brief time a visual notification of a relevant event, like a new download.
+   */
+  showEventNotification: function DIV_showEventNotification()
+  {
+    if (!this._initialized) {
+      return;
+    }
+
+    function DIV_SEN_callback() {
+      if (this._notificationTimeout) {
+        clearTimeout(this._notificationTimeout);
+      }
+
+      let indicator = this.indicator;
+      indicator.setAttribute("notification", "true");
+      this._notificationTimeout = setTimeout(
+        function () indicator.removeAttribute("notification"), 1000);
+    }
+
+    this._ensureOperational(DIV_SEN_callback.bind(this));
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsIndicatorData
+
+  /**
+   * Indicates whether the indicator should be shown because there are some
+   * downloads to be displayed.
+   */
+  set hasDownloads(aValue)
+  {
+    if (this._hasDownloads != aValue) {
+      this._hasDownloads = aValue;
+
+      // If there is at least one download, ensure that the view elements are
+      // loaded before determining the position of the downloads button.
+      if (aValue) {
+        this._ensureOperational(function() DownloadsButton.updatePosition());
+      } else {
+        DownloadsButton.updatePosition();
+      }
+    }
+    return aValue;
+  },
+  get hasDownloads()
+  {
+    return this._hasDownloads;
+  },
+  _hasDownloads: false,
+
+  /**
+   * Status text displayed in the indicator.  If this is set to an empty value,
+   * then the small downloads icon is displayed instead of the text.
+   */
+  set counter(aValue)
+  {
+    if (!this._operational) {
+      return this._counter;
+    }
+
+    if (this._counter !== aValue) {
+      this._counter = aValue;
+      if (this._counter)
+        this.indicator.setAttribute("counter", "true");
+      else
+        this.indicator.removeAttribute("counter");
+      // We have to set the attribute instead of using the property because the
+      // XBL binding isn't applied if the element is invisible for any reason.
+      this._indicatorCounter.setAttribute("value", aValue);
+    }
+    return aValue;
+  },
+  _counter: null,
+
+  /**
+   * Progress indication to display, from 0 to 100, or -1 if unknown.  The
+   * progress bar is hidden if the current progress is unknown and no status
+   * text is set in the "counter" property.
+   */
+  set percentComplete(aValue)
+  {
+    if (!this._operational) {
+      return this._percentComplete;
+    }
+
+    if (this._percentComplete !== aValue) {
+      this._percentComplete = aValue;
+      if (this._percentComplete >= 0)
+        this.indicator.setAttribute("progress", "true");
+      else
+        this.indicator.removeAttribute("progress");
+      // We have to set the attribute instead of using the property because the
+      // XBL binding isn't applied if the element is invisible for any reason.
+      this._indicatorProgress.setAttribute("value", Math.max(aValue, 0));
+    }
+    return aValue;
+  },
+  _percentComplete: null,
+
+  /**
+   * Indicates whether the progress won't advance because of a paused state.
+   * Setting this property forces a paused progress bar to be displayed, even if
+   * the current progress information is unavailable.
+   */
+  set paused(aValue)
+  {
+    if (!this._operational) {
+      return this._paused;
+    }
+
+    if (this._paused != aValue) {
+      this._paused = aValue;
+      if (this._paused) {
+        this.indicator.setAttribute("paused", "true")
+      } else {
+        this.indicator.removeAttribute("paused");
+      }
+    }
+    return aValue;
+  },
+  _paused: false,
+
+  /**
+   * Set when the indicator should draw user attention to itself.
+   */
+  set attention(aValue)
+  {
+    if (!this._operational) {
+      return this._attention;
+    }
+
+    if (this._attention != aValue) {
+      this._attention = aValue;
+      if (aValue) {
+        this.indicator.setAttribute("attention", "true")
+      } else {
+        this.indicator.removeAttribute("attention");
+      }
+    }
+    return aValue;
+  },
+  _attention: false,
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// User interface event functions
+
+  onWindowUnload: function DIV_onWindowUnload()
+  {
+    // This function is registered as an event listener, we can't use "this".
+    DownloadsIndicatorView.ensureTerminated();
+  },
+
+  onCommand: function DIV_onCommand(aEvent)
+  {
+    if (DownloadsCommon.useToolkitUI) {
+      // The panel won't suppress attention for us, we need to clear now.
+      DownloadsCommon.indicatorData.attention = false;
+    }
+
+    BrowserDownloadsUI();
+
+    aEvent.stopPropagation();
+  },
+
+  onDragOver: function DIV_onDragOver(aEvent)
+  {
+    browserDragAndDrop.dragOver(aEvent);
+  },
+
+  onDragExit: function () { },
+
+  onDrop: function DIV_onDrop(aEvent)
+  {
+    let name = {};
+    let url = browserDragAndDrop.drop(aEvent, name);
+    if (url) {
+      saveURL(url, name.value, null, true, true);
+      aEvent.preventDefault();
+    }
+  },
+
+  /**
+   * Returns a reference to the main indicator element, or null if the element
+   * is not present in the browser window yet.
+   */
+  get indicator()
+  {
+    let indicator = document.getElementById("downloads-indicator");
+    if (!indicator) {
+      return null;
+    }
+
+    // Once the element is loaded, it will never be unloaded.
+    delete this.indicator;
+    return this.indicator = indicator;
+  },
+
+  get indicatorAnchor()
+  {
+    delete this.indicatorAnchor;
+    return this.indicatorAnchor =
+      document.getElementById("downloads-indicator-anchor");
+  },
+
+  get _indicatorCounter()
+  {
+    delete this._indicatorCounter;
+    return this._indicatorCounter =
+      document.getElementById("downloads-indicator-counter");
+  },
+
+  get _indicatorProgress()
+  {
+    delete this._indicatorProgress;
+    return this._indicatorProgress =
+      document.getElementById("downloads-indicator-progress");
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/content/indicatorOverlay.xul
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+# -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# vim: set ts=2 et sw=2 tw=80:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+<?xml-stylesheet href="chrome://browser/content/downloads/downloads.css"?>
+<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
+
+<!DOCTYPE overlay SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
+
+<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+         id="indicatorOverlay">
+
+  <popupset>
+    <!-- The downloads indicator is placed in its final toolbar location
+         programmatically, and can be shown temporarily even when its
+         placeholder is removed from the toolbars.  Its initial location within
+         the document must not be a toolbar or the toolbar palette, otherwise the
+         toolbar handling code could remove it from the document. -->
+    <toolbarbutton id="downloads-indicator"
+                   class="toolbarbutton-1 chromeclass-toolbar-additional"
+                   tooltiptext="&indicator.tooltiptext;"
+                   collapsed="true"
+                   oncommand="DownloadsIndicatorView.onCommand(event);"
+                   ondrop="DownloadsIndicatorView.onDrop(event);"
+                   ondragover="DownloadsIndicatorView.onDragOver(event);"
+                   ondragenter="DownloadsIndicatorView.onDragOver(event);"
+                   ondragleave="DownloadsIndicatorView.onDragLeave(event);">
+      <!-- The panel's anchor area is smaller than the outer button, but must
+           always be visible and must not move or resize when the indicator
+           state changes, otherwise the panel could change its position or lose
+           its arrow unexpectedly. -->
+      <stack id="downloads-indicator-anchor"
+             class="toolbarbutton-icon">
+        <vbox id="downloads-indicator-progress-area"
+              pack="center">
+          <description id="downloads-indicator-counter"/>
+          <progressmeter id="downloads-indicator-progress"
+                         class="plain"
+                         min="0"
+                         max="100"/>
+        </vbox>
+        <vbox id="downloads-indicator-icon"/>
+        <vbox id="downloads-indicator-notification"/>
+      </stack>
+    </toolbarbutton>
+  </popupset>
+</overlay>
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/jar.mn
@@ -0,0 +1,7 @@
+browser.jar:
+*       content/browser/downloads/download.xml           (content/download.xml)
+        content/browser/downloads/downloads.css          (content/downloads.css)
+        content/browser/downloads/downloads.js           (content/downloads.js)
+*       content/browser/downloads/downloadsOverlay.xul   (content/downloadsOverlay.xul)
+        content/browser/downloads/indicator.js           (content/indicator.js)
+*       content/browser/downloads/indicatorOverlay.xul   (content/indicatorOverlay.xul)
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/src/BrowserDownloads.manifest
@@ -0,0 +1,4 @@
+component {49507fe5-2cee-4824-b6a3-e999150ce9b8} DownloadsStartup.js
+contract @mozilla.org/browser/downloadsstartup;1 {49507fe5-2cee-4824-b6a3-e999150ce9b8}
+category app-startup DownloadsStartup service,@mozilla.org/browser/downloadsstartup;1
+component {4d99321e-d156-455b-81f7-e7aa2308134f} DownloadsUI.js
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/src/DownloadsCommon.jsm
@@ -0,0 +1,1230 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = [
+  "DownloadsCommon",
+];
+
+/**
+ * Handles the Downloads panel shared methods and data access.
+ *
+ * This file includes the following constructors and global objects:
+ *
+ * DownloadsCommon
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ *
+ * DownloadsData
+ * Retrieves the list of past and completed downloads from the underlying
+ * Download Manager data, and provides asynchronous notifications allowing
+ * to build a consistent view of the available data.
+ *
+ * DownloadsDataItem
+ * Represents a single item in the list of downloads.  This object either wraps
+ * an existing nsIDownload from the Download Manager, or provides the same
+ * information read directly from the downloads database, with the possibility
+ * of querying the nsIDownload lazily, for performance reasons.
+ *
+ * DownloadsIndicatorData
+ * This object registers itself with DownloadsData as a view, and transforms the
+ * notifications it receives into overall status data, that is then broadcast to
+ * the registered download status indicators.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue",
+                                   "@mozilla.org/browser/browserglue;1",
+                                   "nsIBrowserGlue");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                  "resource://gre/modules/NetUtil.jsm");
+
+const nsIDM = Ci.nsIDownloadManager;
+
+const kDownloadsStringBundleUrl =
+  "chrome://browser/locale/downloads/downloads.properties";
+
+const kDownloadsStringsRequiringFormatting = {
+  sizeWithUnits: true,
+  shortTimeLeftSeconds: true,
+  shortTimeLeftMinutes: true,
+  shortTimeLeftHours: true,
+  shortTimeLeftDays: true,
+  statusSeparator: true,
+  statusSeparatorBeforeNumber: true,
+  fileExecutableSecurityWarning: true
+};
+
+XPCOMUtils.defineLazyGetter(this, "DownloadsLocalFileCtor", function () {
+  return Components.Constructor("@mozilla.org/file/local;1",
+                                "nsILocalFile", "initWithPath");
+});
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsCommon
+
+/**
+ * This object is exposed directly to the consumers of this JavaScript module,
+ * and provides shared methods for all the instances of the user interface.
+ */
+const DownloadsCommon = {
+  /**
+   * Returns an object whose keys are the string names from the downloads string
+   * bundle, and whose values are either the translated strings or functions
+   * returning formatted strings.
+   */
+  get strings()
+  {
+    let strings = {};
+    let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
+    let enumerator = sb.getSimpleEnumeration();
+    while (enumerator.hasMoreElements()) {
+      let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
+      let stringName = string.key;
+      if (stringName in kDownloadsStringsRequiringFormatting) {
+        strings[stringName] = function () {
+          // Convert "arguments" to a real array before calling into XPCOM.
+          return sb.formatStringFromName(stringName,
+                                         Array.slice(arguments, 0),
+                                         arguments.length);
+        };
+      } else {
+        strings[stringName] = string.value;
+      }
+    }
+    delete this.strings;
+    return this.strings = strings;
+  },
+
+  /**
+   * Generates a very short string representing the given time left.
+   *
+   * @param aSeconds
+   *        Value to be formatted.  It represents the number of seconds, it must
+   *        be positive but does not need to be an integer.
+   *
+   * @return Formatted string, for example "30s" or "2h".  The returned value is
+   *         maximum three characters long, at least in English.
+   */
+  formatTimeLeft: function DC_formatTimeLeft(aSeconds)
+  {
+    // Decide what text to show for the time
+    let seconds = Math.round(aSeconds);
+    if (!seconds) {
+      return "";
+    } else if (seconds <= 30) {
+      return DownloadsCommon.strings["shortTimeLeftSeconds"](seconds);
+    }
+    let minutes = Math.round(aSeconds / 60);
+    if (minutes < 60) {
+      return DownloadsCommon.strings["shortTimeLeftMinutes"](minutes);
+    }
+    let hours = Math.round(minutes / 60);
+    if (hours < 48) { // two days
+      return DownloadsCommon.strings["shortTimeLeftHours"](hours);
+    }
+    let days = Math.round(hours / 24);
+    return DownloadsCommon.strings["shortTimeLeftDays"](Math.min(days, 99));
+  },
+
+  /**
+   * Indicates whether we should show the full Download Manager window interface
+   * instead of the simplified panel interface.  The behavior of downloads
+   * across browsing session is consistent with the selected interface.
+   */
+  get useToolkitUI()
+  {
+    try {
+      return Services.prefs.getBoolPref("browser.download.useToolkitUI");
+    } catch (ex) { }
+    return false;
+  },
+
+  /**
+   * Returns a reference to the DownloadsData singleton.
+   *
+   * This does not need to be a lazy getter, since no initialization is required
+   * at present.
+   */
+  get data() DownloadsData,
+
+  /**
+   * Returns a reference to the DownloadsData singleton.
+   *
+   * This does not need to be a lazy getter, since no initialization is required
+   * at present.
+   */
+  get indicatorData() DownloadsIndicatorData
+};
+
+/**
+ * Returns true if we are executing on Windows Vista or a later version.
+ */
+XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
+  let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+  if (os != "WINNT") {
+    return false;
+  }
+  let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
+  return parseFloat(sysInfo.getProperty("version")) >= 6;
+});
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsData
+
+/**
+ * Retrieves the list of past and completed downloads from the underlying
+ * Download Manager data, and provides asynchronous notifications allowing to
+ * build a consistent view of the available data.
+ *
+ * This object responds to real-time changes in the underlying Download Manager
+ * data.  For example, the deletion of one or more downloads is notified through
+ * the nsIObserver interface, while any state or progress change is notified
+ * through the nsIDownloadProgressListener interface.
+ *
+ * Note that using this object does not automatically start the Download Manager
+ * service.  Consumers will see an empty list of downloads until the service is
+ * actually started.  This is useful to display a neutral progress indicator in
+ * the main browser window until the autostart timeout elapses.
+ */
+const DownloadsData = {
+  /**
+   * Starts receiving events for current downloads.
+   *
+   * @param aDownloadManagerService
+   *        Reference to the service implementing nsIDownloadManager.  We need
+   *        this because getService isn't available for us when this method is
+   *        called, and we must ensure to register our listeners before the
+   *        getService call for the Download Manager returns.
+   */
+  initializeDataLink: function DD_initializeDataLink(aDownloadManagerService)
+  {
+    // Start receiving real-time events.
+    aDownloadManagerService.addListener(this);
+    Services.obs.addObserver(this, "download-manager-remove-download", false);
+    Services.obs.addObserver(this, "download-manager-database-type-changed",
+                             false);
+  },
+
+  /**
+   * Stops receiving events for current downloads and cancels any pending read.
+   */
+  terminateDataLink: function DD_terminateDataLink()
+  {
+    this._terminateDataAccess();
+
+    // Stop receiving real-time events.
+    Services.obs.removeObserver(this, "download-manager-database-type-changed");
+    Services.obs.removeObserver(this, "download-manager-remove-download");
+    Services.downloads.removeListener(this);
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Registration of views
+
+  /**
+   * Array of view objects that should be notified when the available download
+   * data changes.
+   */
+  _views: [],
+
+  /**
+   * Adds an object to be notified when the available download data changes.
+   * The specified object is initialized with the currently available downloads.
+   *
+   * @param aView
+   *        DownloadsView object to be added.  This reference must be passed to
+   *        removeView before termination.
+   */
+  addView: function DD_addView(aView)
+  {
+    this._views.push(aView);
+    this._updateView(aView);
+  },
+
+  /**
+   * Removes an object previously added using addView.
+   *
+   * @param aView
+   *        DownloadsView object to be removed.
+   */
+  removeView: function DD_removeView(aView)
+  {
+    let index = this._views.indexOf(aView);
+    if (index != -1) {
+      this._views.splice(index, 1);
+    }
+  },
+
+  /**
+   * Ensures that the currently loaded data is added to the specified view.
+   *
+   * @param aView
+   *        DownloadsView object to be initialized.
+   */
+  _updateView: function DD_updateView(aView)
+  {
+    // Indicate to the view that a batch loading operation is in progress.
+    aView.onDataLoadStarting();
+
+    // Sort backwards by download identifier, ensuring that the most recent
+    // downloads are added first regardless of their state.
+    let loadedItemsArray = [dataItem
+                            for each (dataItem in this.dataItems)
+                            if (dataItem)];
+    loadedItemsArray.sort(function(a, b) b.downloadId - a.downloadId);
+    loadedItemsArray.forEach(
+      function (dataItem) aView.onDataItemAdded(dataItem, false)
+    );
+
+    // Notify the view that all data is available unless loading is in progress.
+    if (!this._pendingStatement) {
+      aView.onDataLoadCompleted();
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// In-memory downloads data store
+
+  /**
+   * Object containing all the available DownloadsDataItem objects, indexed by
+   * their numeric download identifier.  The identifiers of downloads that have
+   * been removed from the Download Manager data are still present, however the
+   * associated objects are replaced with the value "null".  This is required to
+   * prevent race conditions when populating the list asynchronously.
+   */
+  dataItems: {},
+
+  /**
+   * While operating in Private Browsing Mode, persistent data items are parked
+   * here until we return to the normal mode.
+   */
+  _persistentDataItems: {},
+
+  /**
+   * Clears the loaded data.
+   */
+  clear: function DD_clear()
+  {
+    this._terminateDataAccess();
+    this.dataItems = {};
+  },
+
+  /**
+   * Returns the data item associated with the provided source object.  The
+   * source can be a download object that we received from the Download Manager
+   * because of a real-time notification, or a row from the downloads database,
+   * during the asynchronous data load.
+   *
+   * In case we receive download status notifications while we are still
+   * populating the list of downloads from the database, we want the real-time
+   * status to take precedence over the state that is read from the database,
+   * which might be older.  This is achieved by creating the download item if
+   * it's not already in the list, but never updating the returned object using
+   * the data from the database, if the object already exists.
+   *
+   * @param aSource
+   *        Object containing the data with which the item should be initialized
+   *        if it doesn't already exist in the list.  This should implement
+   *        either nsIDownload or mozIStorageRow.  If the item exists, this
+   *        argument is only used to retrieve the download identifier.
+   * @param aMayReuseId
+   *        If false, indicates that the download should not be added if a
+   *        download with the same identifier was removed in the meantime.  This
+   *        ensures that, while loading the list asynchronously, downloads that
+   *        have been removed in the meantime do no reappear inadvertently.
+   *
+   * @return New or existing data item, or null if the item was deleted from the
+   *         list of available downloads.
+   */
+  _getOrAddDataItem: function DD_getOrAddDataItem(aSource, aMayReuseId)
+  {
+    let downloadId = (aSource instanceof Ci.nsIDownload)
+                     ? aSource.id
+                     : aSource.getResultByName("id");
+    if (downloadId in this.dataItems) {
+      let existingItem = this.dataItems[downloadId];
+      if (existingItem || !aMayReuseId) {
+        // Returns null if the download was removed and we can't reuse the item.
+        return existingItem;
+      }
+    }
+
+    let dataItem = new DownloadsDataItem(aSource);
+    this.dataItems[downloadId] = dataItem;
+
+    // Create the view items before returning.
+    let addToStartOfList = aSource instanceof Ci.nsIDownload;
+    this._views.forEach(
+      function (view) view.onDataItemAdded(dataItem, addToStartOfList)
+    );
+    return dataItem;
+  },
+
+  /**
+   * Removes the data item with the specified identifier.
+   *
+   * This method can be called at most once per download identifier.
+   */
+  _removeDataItem: function DD_removeDataItem(aDownloadId)
+  {
+    if (aDownloadId in this.dataItems) {
+      let dataItem = this.dataItems[aDownloadId];
+      this._views.forEach(
+        function (view) view.onDataItemRemoved(dataItem)
+      );
+    }
+    this.dataItems[aDownloadId] = null;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Persistent data loading
+
+  /**
+   * Asynchronous database statement used to read the list of downloads.
+   */
+  _statement: null,
+
+  /**
+   * Represents an executing statement, allowing its cancellation.
+   */
+  _pendingStatement: null,
+
+  /**
+   * Indicates which kind of items from the persistent downloads database have
+   * been fully loaded in memory and are available to the views.  This can
+   * assume the value of one of the kLoad constants.
+   */
+  _loadState: 0,
+
+  /** No downloads have been fully loaded yet. */
+  get kLoadNone() 0,
+  /** All the active downloads in the database are loaded in memory. */
+  get kLoadActive() 1,
+  /** All the downloads in the database are loaded in memory. */
+  get kLoadAll() 2,
+
+  /**
+   * Reloads the specified kind of downloads from the persistent database.  This
+   * method must only be called when Private Browsing Mode is disabled.
+   *
+   * @param aActiveOnly
+   *        True to load only active downloads from the database.
+   */
+  ensurePersistentDataLoaded:
+  function DD_ensurePersistentDataLoaded(aActiveOnly)
+  {
+    if (this._pendingStatement) {
+      // We are already in the process of reloading all downloads.
+      return;
+    }
+
+    if (aActiveOnly) {
+      if (this._loadState == this.kLoadNone) {
+        // Indicate to the views that a batch loading operation is in progress.
+        this._views.forEach(
+          function (view) view.onDataLoadStarting()
+        );
+
+        // Reload the list using the Download Manager service.
+        let downloads = Services.downloads.activeDownloads;
+        while (downloads.hasMoreElements()) {
+          let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
+          this._getOrAddDataItem(download, true);
+        }
+        this._loadState = this.kLoadActive;
+
+        // Indicate to the views that the batch loading operation is complete.
+        this._views.forEach(
+          function (view) view.onDataLoadCompleted()
+        );
+      }
+    } else {
+      if (this._loadState != this.kLoadAll) {
+        // Reload the list from the database asynchronously.
+        this._statement = Services.downloads.DBConnection.createAsyncStatement(
+                                "SELECT * FROM moz_downloads ORDER BY id DESC");
+        this._pendingStatement = this._statement.executeAsync(this);
+      }
+    }
+  },
+
+  /**
+   * Cancels any pending data access and ensures views are notified.
+   */
+  _terminateDataAccess: function DD_terminateDataAccess()
+  {
+    if (this._pendingStatement) {
+      this._pendingStatement.cancel();
+      this._pendingStatement = null;
+    }
+    if (this._statement) {
+      this._statement.finalize();
+      this._statement = null;
+    }
+
+    // Close all the views on the current data.  Create a copy of the array
+    // because some views might unregister while processing this event.
+    Array.slice(this._views, 0).forEach(
+      function (view) view.onDataInvalidated()
+    );
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageStatementCallback
+
+  handleResult: function DD_handleResult(aResultSet)
+  {
+    for (let row = aResultSet.getNextRow();
+         row;
+         row = aResultSet.getNextRow()) {
+      // Add the download to the list and initialize it with the data we read,
+      // unless we already received a notification providing more reliable
+      // information for this download.
+      this._getOrAddDataItem(row, false);
+    }
+  },
+
+  handleError: function DD_handleError(aError)
+  {
+    Cu.reportError("Database statement execution error (" + aError.result +
+                   "): " + aError.message);
+  },
+
+  handleCompletion: function DD_handleCompletion(aReason)
+  {
+    this._pendingStatement = null;
+
+    // To ensure that we don't inadvertently delete more downloads from the
+    // database than needed on shutdown, we should update the load state only if
+    // the operation completed successfully.
+    if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+      this._loadState = this.kLoadAll;
+    }
+
+    // Indicate to the views that the batch loading operation is complete, even
+    // if the lookup failed or was canceled.  The only possible glitch happens
+    // in case the database backend changes while loading data, when the views
+    // would open and immediately close.  This case is rare enough not to need a
+    // special treatment.
+    this._views.forEach(
+      function (view) view.onDataLoadCompleted()
+    );
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIObserver
+
+  observe: function DD_observe(aSubject, aTopic, aData)
+  {
+    switch (aTopic) {
+      case "download-manager-remove-download":
+        // If a single download was removed, remove the corresponding data item.
+        if (aSubject) {
+          this._removeDataItem(aSubject.QueryInterface(Ci.nsISupportsPRUint32));
+          break;
+        }
+
+        // Multiple downloads have been removed.  Iterate over known downloads
+        // and remove those that don't exist anymore.
+        for each (let dataItem in this.dataItems) {
+          if (dataItem) {
+            try {
+              Services.downloads.getDownload(dataItem.downloadId);
+            } catch (ex) {
+              this._removeDataItem(dataItem.downloadId);
+            }
+          }
+        }
+        break;
+
+      case "download-manager-database-type-changed":
+        let pbs = Cc["@mozilla.org/privatebrowsing;1"]
+                  .getService(Ci.nsIPrivateBrowsingService);
+        if (pbs.privateBrowsingEnabled) {
+          // Save a reference to the persistent store before terminating access.
+          this._persistentDataItems = this.dataItems;
+          this.clear();
+        } else {
+          // Terminate data access, then restore the persistent store.
+          this.clear();
+          this.dataItems = this._persistentDataItems;
+          this._persistentDataItems = null;
+        }
+        // Reinitialize the views with the current items.  View data has been
+        // already invalidated by the previous calls.
+        this._views.forEach(this._updateView, this);
+        break;
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIDownloadProgressListener
+
+  onDownloadStateChange: function DD_onDownloadStateChange(aState, aDownload)
+  {
+    // When a new download is added, it may have the same identifier of a
+    // download that we previously deleted during this session, and we also
+    // want to provide a visible indication that the download started.
+    let isNew = aState == nsIDM.DOWNLOAD_NOTSTARTED ||
+                aState == nsIDM.DOWNLOAD_QUEUED;
+
+    let dataItem = this._getOrAddDataItem(aDownload, isNew);
+    if (!dataItem) {
+      return;
+    }
+
+    dataItem.state = aDownload.state;
+    dataItem.referrer = aDownload.referrer && aDownload.referrer.spec;
+    dataItem.resumable = aDownload.resumable;
+    dataItem.startTime = Math.round(aDownload.startTime / 1000);
+    dataItem.currBytes = aDownload.amountTransferred;
+    dataItem.maxBytes = aDownload.size;
+
+    this._views.forEach(
+      function (view) view.getViewItem(dataItem).onStateChange()
+    );
+
+    if (isNew && !dataItem.newDownloadNotified) {
+      dataItem.newDownloadNotified = true;
+      this._notifyNewDownload();
+    }
+  },
+
+  onProgressChange: function DD_onProgressChange(aWebProgress, aRequest,
+                                                  aCurSelfProgress,
+                                                  aMaxSelfProgress,
+                                                  aCurTotalProgress,
+                                                  aMaxTotalProgress, aDownload)
+  {
+    let dataItem = this._getOrAddDataItem(aDownload, false);
+    if (!dataItem) {
+      return;
+    }
+
+    dataItem.currBytes = aDownload.amountTransferred;
+    dataItem.maxBytes = aDownload.size;
+    dataItem.speed = aDownload.speed;
+    dataItem.percentComplete = aDownload.percentComplete;
+
+    this._views.forEach(
+      function (view) view.getViewItem(dataItem).onProgressChange()
+    );
+  },
+
+  onStateChange: function () { },
+
+  onSecurityChange: function () { },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Notifications sent to the most recent browser window only
+
+  /**
+   * Set to true after the first download in the session caused the downloads
+   * panel to be displayed.
+   */
+  firstDownloadShown: false,
+
+  /**
+   * Displays a new download notification in the most recent browser window, if
+   * one is currently available.
+   */
+  _notifyNewDownload: function DD_notifyNewDownload()
+  {
+    if (DownloadsCommon.useToolkitUI) {
+      return;
+    }
+
+    // Show the panel in the most recent browser window, if present.
+    let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
+    if (!browserWin) {
+      return;
+    }
+
+    browserWin.focus();
+    if (this.firstDownloadShown) {
+      // For new downloads after the first one in the session, don't show the
+      // panel automatically, but provide a visible notification in the topmost
+      // browser window, if the status indicator is already visible.
+      browserWin.DownloadsIndicatorView.showEventNotification();
+      return;
+    }
+    this.firstDownloadShown = true;
+    browserWin.DownloadsPanel.showPanel();
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsDataItem
+
+/**
+ * Represents a single item in the list of downloads.  This object either wraps
+ * an existing nsIDownload from the Download Manager, or provides the same
+ * information read directly from the downloads database, with the possibility
+ * of querying the nsIDownload lazily, for performance reasons.
+ *
+ * @param aSource
+ *        Object containing the data with which the item should be initialized.
+ *        This should implement either nsIDownload or mozIStorageRow.
+ */
+function DownloadsDataItem(aSource)
+{
+  if (aSource instanceof Ci.nsIDownload) {
+    this._initFromDownload(aSource);
+  } else {
+    this._initFromDataRow(aSource);
+  }
+}
+
+DownloadsDataItem.prototype = {
+  /**
+   * Initializes this object from a download object of the Download Manager.
+   *
+   * The endTime property is initialized to the current date and time.
+   *
+   * @param aDownload
+   *        The nsIDownload with the current state.
+   */
+  _initFromDownload: function DDI_initFromDownload(aDownload)
+  {
+    this.download = aDownload;
+
+    // Fetch all the download properties eagerly.
+    this.downloadId = aDownload.id;
+    this.file = aDownload.target.spec;
+    this.target = aDownload.displayName;
+    this.uri = aDownload.source.spec;
+    this.referrer = aDownload.referrer && aDownload.referrer.spec;
+    this.state = aDownload.state;
+    this.startTime = Math.round(aDownload.startTime / 1000);
+    this.endTime = Date.now();
+    this.currBytes = aDownload.amountTransferred;
+    this.maxBytes = aDownload.size;
+    this.resumable = aDownload.resumable;
+    this.speed = aDownload.speed;
+    this.percentComplete = aDownload.percentComplete;
+  },
+
+  /**
+   * Initializes this object from a data row in the downloads database, without
+   * querying the associated nsIDownload object, to improve performance when
+   * loading the list of downloads asynchronously.
+   *
+   * When this object is initialized in this way, accessing the "download"
+   * property loads the underlying nsIDownload object synchronously, and should
+   * be avoided unless the object is really required.
+   *
+   * @param aStorageRow
+   *        The mozIStorageRow from the downloads database.
+   */
+  _initFromDataRow: function DDI_initFromDataRow(aStorageRow)
+  {
+    // Get the download properties from the data row.
+    this.downloadId = aStorageRow.getResultByName("id");
+    this.file = aStorageRow.getResultByName("target");
+    this.target = aStorageRow.getResultByName("name");
+    this.uri = aStorageRow.getResultByName("source");
+    this.referrer = aStorageRow.getResultByName("referrer");
+    this.state = aStorageRow.getResultByName("state");
+    this.startTime = Math.round(aStorageRow.getResultByName("startTime") / 1000);
+    this.endTime = Math.round(aStorageRow.getResultByName("endTime") / 1000);
+    this.currBytes = aStorageRow.getResultByName("currBytes");
+    this.maxBytes = aStorageRow.getResultByName("maxBytes");
+
+    // Allows accessing the underlying download object lazily.
+    XPCOMUtils.defineLazyGetter(this, "download", function ()
+                                Services.downloads.getDownload(this.downloadId));
+
+    // Now we have to determine if the download is resumable, but don't want to
+    // access the underlying download object unnecessarily.  The only case where
+    // the property is relevant is when we are currently downloading data, and
+    // in this case the download object is already loaded in memory or will be
+    // loaded very soon in any case.  In all the other cases, including a paused
+    // download, we assume that the download is resumable.  The property will be
+    // updated as soon as the underlying download state changes.
+    if (this.state == nsIDM.DOWNLOAD_DOWNLOADING) {
+      this.resumable = this.download.resumable;
+    } else {
+      this.resumable = true;
+    }
+
+    // Compute the other properties without accessing the download object.
+    this.speed = 0;
+    this.percentComplete = this.maxBytes <= 0
+                           ? -1
+                           : Math.round(this.currBytes / this.maxBytes * 100);
+  },
+
+  /**
+   * Indicates whether the download is proceeding normally, and not finished
+   * yet.  This includes paused downloads.  When this property is true, the
+   * "progress" property represents the current progress of the download.
+   */
+  get inProgress()
+  {
+    return [
+      nsIDM.DOWNLOAD_NOTSTARTED,
+      nsIDM.DOWNLOAD_QUEUED,
+      nsIDM.DOWNLOAD_DOWNLOADING,
+      nsIDM.DOWNLOAD_PAUSED,
+      nsIDM.DOWNLOAD_SCANNING,
+    ].indexOf(this.state) != -1;
+  },
+
+  /**
+   * This is true during the initial phases of a download, before the actual
+   * download of data bytes starts.
+   */
+  get starting()
+  {
+    return this.state == nsIDM.DOWNLOAD_NOTSTARTED ||
+           this.state == nsIDM.DOWNLOAD_QUEUED;
+  },
+
+  /**
+   * Indicates whether the download is paused.
+   */
+  get paused()
+  {
+    return this.state == nsIDM.DOWNLOAD_PAUSED;
+  },
+
+  /**
+   * Indicates whether the download is in a final state, either because it
+   * completed successfully or because it was blocked.
+   */
+  get done()
+  {
+    return [
+      nsIDM.DOWNLOAD_FINISHED,
+      nsIDM.DOWNLOAD_BLOCKED_PARENTAL,
+      nsIDM.DOWNLOAD_BLOCKED_POLICY,
+      nsIDM.DOWNLOAD_DIRTY,
+    ].indexOf(this.state) != -1;
+  },
+
+  /**
+   * Indicates whether the download is finished and can be opened.
+   */
+  get openable()
+  {
+    return this.state == nsIDM.DOWNLOAD_FINISHED;
+  },
+
+  /**
+   * Indicates whether the download stopped because of an error, and can be
+   * resumed manually.
+   */
+  get canRetry()
+  {
+    return this.state == nsIDM.DOWNLOAD_CANCELED ||
+           this.state == nsIDM.DOWNLOAD_FAILED;
+  },
+
+  /**
+   * Returns the nsILocalFile for the download target.
+   *
+   * @throws if the native path is not valid.  This can happen if the same
+   *         profile is used on different platforms, for example if a native
+   *         Windows path is stored and then the item is accessed on a Mac.
+   */
+  get localFile()
+  {
+    // The download database may contain targets stored as file URLs or native
+    // paths.  This can still be true for previously stored items, even if new
+    // items are stored using their file URL.  See also bug 239948 comment 12.
+    if (/^file:/.test(this.file)) {
+      // Assume the file URL we obtained from the downloads database or from the
+      // "spec" property of the target has the UTF-8 charset.
+      let fileUrl = NetUtil.newURI(this.file).QueryInterface(Ci.nsIFileURL);
+      return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
+    } else {
+      // The downloads database contains a native path.  Try to create a local
+      // file, though this may throw an exception if the path is invalid.
+      return new DownloadsLocalFileCtor(this.file);
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsIndicatorData
+
+/**
+ * This object registers itself with DownloadsData as a view, and transforms the
+ * notifications it receives into overall status data, that is then broadcast to
+ * the registered download status indicators.
+ *
+ * Note that using this object does not automatically start the Download Manager
+ * service.  Consumers will see an empty list of downloads until the service is
+ * actually started.  This is useful to display a neutral progress indicator in
+ * the main browser window until the autostart timeout elapses.
+ */
+const DownloadsIndicatorData = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Registration of views
+
+  /**
+   * Array of view objects that should be notified when the available status
+   * data changes.
+   */
+  _views: [],
+
+  /**
+   * Adds an object to be notified when the available status data changes.
+   * The specified object is initialized with the currently available status.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be added.  This reference must be
+   *        passed to removeView before termination.
+   */
+  addView: function DID_addView(aView)
+  {
+    // Start receiving events when the first of our views is registered.
+    if (this._views.length == 0) {
+      DownloadsCommon.data.addView(this);
+    }
+
+    this._views.push(aView);
+    this.refreshView(aView);
+  },
+
+  /**
+   * Updates the properties of an object previously added using addView.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be updated.
+   */
+  refreshView: function DID_refreshView(aView)
+  {
+    // Update immediately even if we are still loading data asynchronously.
+    this._refreshProperties();
+    this._updateView(aView);
+  },
+
+  /**
+   * Removes an object previously added using addView.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be removed.
+   */
+  removeView: function DID_removeView(aView)
+  {
+    let index = this._views.indexOf(aView);
+    if (index != -1) {
+      this._views.splice(index, 1);
+    }
+
+    // Stop receiving events when the last of our views is unregistered.
+    if (this._views.length == 0) {
+      DownloadsCommon.data.removeView(this);
+      this._itemCount = 0;
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Callback functions from DownloadsData
+
+  /**
+   * Indicates whether we are still loading downloads data asynchronously.
+   */
+  _loading: false,
+
+  /**
+   * Called before multiple downloads are about to be loaded.
+   */
+  onDataLoadStarting: function DID_onDataLoadStarting()
+  {
+    this._loading = true;
+  },
+
+  /**
+   * Called after data loading finished.
+   */
+  onDataLoadCompleted: function DID_onDataLoadCompleted()
+  {
+    this._loading = false;
+    this._updateViews();
+  },
+
+  /**
+   * Called when the downloads database becomes unavailable (for example, we
+   * entered Private Browsing Mode and the database backend changed).
+   * References to existing data should be discarded.
+   */
+  onDataInvalidated: function DID_onDataInvalidated()
+  {
+    this._itemCount = 0;
+  },
+
+  /**
+   * Called when a new download data item is available, either during the
+   * asynchronous data load or when a new download is started.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that was just added.
+   * @param aNewest
+   *        When true, indicates that this item is the most recent and should be
+   *        added in the topmost position.  This happens when a new download is
+   *        started.  When false, indicates that the item is the least recent
+   *        with regard to the items that have been already added. The latter
+   *        generally happens during the asynchronous data load.
+   */
+  onDataItemAdded: function DID_onDataItemAdded(aDataItem, aNewest)
+  {
+    this._itemCount++;
+    this._updateViews();
+  },
+
+  /**
+   * Called when a data item is removed, ensures that the widget associated with
+   * the view item is removed from the user interface.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object that is being removed.
+   */
+  onDataItemRemoved: function DID_onDataItemRemoved(aDataItem)
+  {
+    this._itemCount--;
+    this._updateViews();
+  },
+
+  /**
+   * Returns the view item associated with the provided data item for this view.
+   *
+   * @param aDataItem
+   *        DownloadsDataItem object for which the view item is requested.
+   *
+   * @return Object that can be used to notify item status events.
+   */
+  getViewItem: function DID_getViewItem(aDataItem)
+  {
+    return Object.freeze({
+      onStateChange: function DIVI_onStateChange()
+      {
+        if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
+            aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
+          DownloadsIndicatorData.attention = true;
+        }
+
+        // Since the state of a download changed, reset the estimated time left.
+        DownloadsIndicatorData._lastRawTimeLeft = -1;
+        DownloadsIndicatorData._lastTimeLeft = -1;
+
+        DownloadsIndicatorData._updateViews();
+      },
+      onProgressChange: function DIVI_onProgressChange()
+      {
+        DownloadsIndicatorData._updateViews();
+      }
+    });
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Propagation of properties to our views
+
+  // The following properties are updated by _refreshProperties and are then
+  // propagated to the views.  See _refreshProperties for details.
+  _hasDownloads: false,
+  _counter: "",
+  _percentComplete: -1,
+  _paused: false,
+
+  /**
+   * Indicates whether the download indicators should be highlighted.
+   */
+  set attention(aValue)
+  {
+    this._attention = aValue;
+    this._updateViews();
+    return aValue;
+  },
+  _attention: false,
+
+  /**
+   * Indicates whether the user is interacting with downloads, thus the
+   * attention indication should not be shown even if requested.
+   */
+  set attentionSuppressed(aValue)
+  {
+    this._attentionSuppressed = aValue;
+    this._attention = false;
+    this._updateViews();
+    return aValue;
+  },
+  _attentionSuppressed: false,
+
+  /**
+   * Computes aggregate values and propagates the changes to our views.
+   */
+  _updateViews: function DID_updateViews()
+  {
+    // Do not update the status indicators during batch loads of download items.
+    if (this._loading) {
+      return;
+    }
+
+    this._refreshProperties();
+    this._views.forEach(this._updateView, this);
+  },
+
+  /**
+   * Updates the specified view with the current aggregate values.
+   *
+   * @param aView
+   *        DownloadsIndicatorView object to be updated.
+   */
+  _updateView: function DID_updateView(aView)
+  {
+    aView.hasDownloads = this._hasDownloads;
+    aView.counter = this._counter;
+    aView.percentComplete = this._percentComplete;
+    aView.paused = this._paused;
+    aView.attention = this._attention && !this._attentionSuppressed;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Property updating based on current download status
+
+  /**
+   * Number of download items that are available to be displayed.
+   */
+  _itemCount: 0,
+
+  /**
+   * Floating point value indicating the last number of seconds estimated until
+   * the longest download will finish.  We need to store this value so that we
+   * don't continuously apply smoothing if the actual download state has not
+   * changed.  This is set to -1 if the previous value is unknown.
+   */
+  _lastRawTimeLeft: -1,
+
+  /**
+   * Last number of seconds estimated until all in-progress downloads with a
+   * known size and speed will finish.  This value is stored to allow smoothing
+   * in case of small variations.  This is set to -1 if the previous value is
+   * unknown.
+   */
+  _lastTimeLeft: -1,
+
+  /**
+   * Update the estimated time until all in-progress downloads will finish.
+   *
+   * @param aSeconds
+   *        Current raw estimate on number of seconds left for all downloads.
+   *        This is a floating point value to help get sub-second accuracy for
+   *        current and future estimates.
+   */
+  _updateTimeLeft: function DID_updateTimeLeft(aSeconds)
+  {
+    // We apply an algorithm similar to the DownloadUtils.getTimeLeft function,
+    // though tailored to a single time estimation for all downloads.  We never
+    // apply sommothing if the new value is less than half the previous value.
+    let shouldApplySmoothing = this._lastTimeLeft >= 0 &&
+                               aSeconds > this._lastTimeLeft / 2;
+    if (shouldApplySmoothing) {
+      // Apply hysteresis to favor downward over upward swings.  Trust only 30%
+      // of the new value if lower, and 10% if higher (exponential smoothing).
+      let (diff = aSeconds - this._lastTimeLeft) {
+        aSeconds = this._lastTimeLeft + (diff < 0 ? .3 : .1) * diff;
+      }
+
+      // If the new time is similar, reuse something close to the last time
+      // left, but subtract a little to provide forward progress.
+      let diff = aSeconds - this._lastTimeLeft;
+      let diffPercent = diff / this._lastTimeLeft * 100;
+      if (Math.abs(diff) < 5 || Math.abs(diffPercent) < 5) {
+        aSeconds = this._lastTimeLeft - (diff < 0 ? .4 : .2);
+      }
+    }
+
+    // In the last few seconds of downloading, we are always subtracting and
+    // never adding to the time left.  Ensure that we never fall below one
+    // second left until all downloads are actually finished.
+    this._lastTimeLeft = Math.max(aSeconds, 1);
+  },
+
+  /**
+   * Computes aggregate values based on the current state of downloads.
+   */
+  _refreshProperties: function DID_refreshProperties()
+  {
+    let numActive = 0;
+    let numPaused = 0;
+    let numScanning = 0;
+    let totalSize = 0;
+    let totalTransferred = 0;
+    let rawTimeLeft = -1;
+
+    // If no download has been loaded, don't use the methods of the Download
+    // Manager service, so that it is not initialized unnecessarily.
+    if (this._itemCount > 0) {
+      let downloads = Services.downloads.activeDownloads;
+      while (downloads.hasMoreElements()) {
+        let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
+        numActive++;
+        switch (download.state) {
+          case nsIDM.DOWNLOAD_PAUSED:
+            numPaused++;
+            break;
+          case nsIDM.DOWNLOAD_SCANNING:
+            numScanning++;
+            break;
+          case nsIDM.DOWNLOAD_DOWNLOADING:
+            if (download.size > 0 && download.speed > 0) {
+              let sizeLeft = download.size - download.amountTransferred;
+              rawTimeLeft = Math.max(rawTimeLeft, sizeLeft / download.speed);
+            }
+            break;
+        }
+        // Only add to total values if we actually know the download size.
+        if (download.size > 0) {
+          totalSize += download.size;
+          totalTransferred += download.amountTransferred;
+        }
+      }
+    }
+
+    // Determine if the indicator should be shown or get attention.
+    this._hasDownloads = (this._itemCount > 0);
+
+    if (numActive == 0 || totalSize == 0 || numActive == numScanning) {
+      // Don't display the current progress.
+      this._percentComplete = -1;
+    } else {
+      // Display the current progress.
+      this._percentComplete = (totalTransferred / totalSize) * 100;
+    }
+
+    // If all downloads are paused, show the progress indicator as paused.
+    this._paused = numActive > 0 && numActive == numPaused;
+
+    // Display the estimated time left, if present.
+    if (rawTimeLeft == -1) {
+      // There are no downloads with a known time left.
+      this._lastRawTimeLeft = -1;
+      this._lastTimeLeft = -1;
+      this._counter = "";
+    } else {
+      // Compute the new time left only if state actually changed.
+      if (this._lastRawTimeLeft != rawTimeLeft) {
+        this._lastRawTimeLeft = rawTimeLeft;
+        this._updateTimeLeft(rawTimeLeft);
+      }
+      this._counter = DownloadsCommon.formatTimeLeft(this._lastTimeLeft);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/src/DownloadsStartup.js
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This component listens to notifications for startup, shutdown and session
+ * restore, controlling which downloads should be loaded from the database.
+ *
+ * To avoid affecting startup performance, this component monitors the current
+ * session restore state, but defers the actual downloads data manipulation
+ * until the Download Manager service is loaded.
+ */
+
+"use strict";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
+                                   "@mozilla.org/browser/sessionstartup;1",
+                                   "nsISessionStartup");
+XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsingService",
+                                   "@mozilla.org/privatebrowsing;1",
+                                   "nsIPrivateBrowsingService");
+
+const kObservedTopics = [
+  "sessionstore-windows-restored",
+  "sessionstore-browser-state-restored",
+  "download-manager-initialized",
+  "download-manager-change-retention",
+  "private-browsing-transition-complete",
+  "browser-lastwindow-close-granted",
+  "quit-application",
+  "profile-change-teardown",
+];
+
+/**
+ * CID of our implementation of nsIDownloadManagerUI.
+ */
+const kDownloadsUICid = Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}");
+
+/**
+ * Contract ID of the service implementing nsIDownloadManagerUI.
+ */
+const kDownloadsUIContractId = "@mozilla.org/download-manager-ui;1";
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsStartup
+
+function DownloadsStartup() { }
+
+DownloadsStartup.prototype = {
+  classID: Components.ID("{49507fe5-2cee-4824-b6a3-e999150ce9b8}"),
+
+  _xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsStartup),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIObserver
+
+  observe: function DS_observe(aSubject, aTopic, aData)
+  {
+    switch (aTopic) {
+      case "app-startup":
+        kObservedTopics.forEach(
+          function (topic) Services.obs.addObserver(this, topic, true),
+          this);
+
+        // Override Toolkit's nsIDownloadManagerUI implementation with our own.
+        // This must be done at application startup and not in the manifest to
+        // ensure that our implementation overrides the original one.
+        Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+                          .registerFactory(kDownloadsUICid, "",
+                                           kDownloadsUIContractId, null);
+        break;
+
+      case "sessionstore-windows-restored":
+      case "sessionstore-browser-state-restored":
+        // Unless there is no saved session, there is a chance that we are
+        // starting up after a restart or a crash.  We should check the disk
+        // database to see if there are completed downloads to recover and show
+        // in the panel, in addition to in-progress downloads.
+        if (gSessionStartup.sessionType != Ci.nsISessionStartup.NO_SESSION) {
+          this._recoverAllDownloads = true;
+        }
+        this._ensureDataLoaded();
+        break;
+
+      case "download-manager-initialized":
+        // Don't initialize the JavaScript data and user interface layer if we
+        // are initializing the Download Manager service during shutdown.
+        if (this._shuttingDown) {
+          break;
+        }
+
+        // Start receiving events for active and new downloads before we return
+        // from this observer function.  We can't defer the execution of this
+        // step, to ensure that we don't lose events raised in the meantime.
+        DownloadsCommon.data.initializeDataLink(
+                             aSubject.QueryInterface(Ci.nsIDownloadManager));
+
+        this._downloadsServiceInitialized = true;
+
+        // Since this notification is generated during the getService call and
+        // we need to get the Download Manager service ourselves, we must post
+        // the handler on the event queue to be executed later.
+        Services.tm.mainThread.dispatch(this._ensureDataLoaded.bind(this),
+                                        Ci.nsIThread.DISPATCH_NORMAL);
+        break;
+
+      case "download-manager-change-retention":
+        // When the panel interface is enabled, we use a different preference to
+        // determine whether downloads should be removed from view as soon as
+        // they are finished.  We do this to allow proper migration to the new
+        // feature when using the same profile on multiple versions of the
+        // product (bug 697678).
+        if (!DownloadsCommon.useToolkitUI) {
+          let removeFinishedDownloads = Services.prefs.getBoolPref(
+                            "browser.download.panel.removeFinishedDownloads");
+          aSubject.QueryInterface(Ci.nsISupportsPRInt32)
+                  .data = removeFinishedDownloads ? 0 : 2;
+        }
+        break;
+
+      case "private-browsing-transition-complete":
+        // Ensure that persistent data is reloaded only when the database
+        // connection is available again.
+        this._ensureDataLoaded();
+        break;
+
+      case "browser-lastwindow-close-granted":
+        // When using the panel interface, downloads that are already completed
+        // should be removed when the last full browser window is closed.  This
+        // event is invoked only if the application is not shutting down yet.
+        // If the Download Manager service is not initialized, we don't want to
+        // initialize it just to clean up completed downloads, because they can
+        // be present only in case there was a browser crash or restart.
+        if (this._downloadsServiceInitialized &&
+            !DownloadsCommon.useToolkitUI) {
+          Services.downloads.cleanUp();
+        }
+        break;
+
+      case "quit-application":
+        // When the application is shutting down, we must free all resources in
+        // addition to cleaning up completed downloads.  If the Download Manager
+        // service is not initialized, we don't want to initialize it just to
+        // clean up completed downloads, because they can be present only in
+        // case there was a browser crash or restart.
+        this._shuttingDown = true;
+        if (!this._downloadsServiceInitialized) {
+          break;
+        }
+
+        DownloadsCommon.data.terminateDataLink();
+
+        // When using the panel interface, downloads that are already completed
+        // should be removed when quitting the application.
+        if (!DownloadsCommon.useToolkitUI && aData != "restart") {
+          this._cleanupOnShutdown = true;
+        }
+        break;
+
+      case "profile-change-teardown":
+        // If we need to clean up, we must do it synchronously after all the
+        // "quit-application" listeners are invoked, so that the Download
+        // Manager service has a chance to pause or cancel in-progress downloads
+        // before we remove completed downloads from the list.  Note that, since
+        // "quit-application" was invoked, we've already exited Private Browsing
+        // Mode, thus we are always working on the disk database.
+        if (this._cleanupOnShutdown) {
+          Services.downloads.cleanUp();
+        }
+        break;
+    }
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Private
+
+  /**
+   * Indicates whether we should load all downloads from the previous session,
+   * including completed items as well as active downloads.
+   */
+  _recoverAllDownloads: false,
+
+  /**
+   * Indicates whether the Download Manager service has been initialized.  This
+   * flag is required because we want to avoid accessing the service immediately
+   * at browser startup.  The service will start when the user first requests a
+   * download, or some time after browser startup.
+   */
+  _downloadsServiceInitialized: false,
+
+  /**
+   * True while we are processing the "quit-application" event, and later.
+   */
+  _shuttingDown: false,
+
+  /**
+   * True during shutdown if we need to remove completed downloads.
+   */
+  _cleanupOnShutdown: false,
+
+  /**
+   * Ensures that persistent download data is reloaded at the appropriate time.
+   */
+  _ensureDataLoaded: function DS_ensureDataLoaded()
+  {
+    if (!this._downloadsServiceInitialized ||
+        gPrivateBrowsingService.privateBrowsingEnabled) {
+      return;
+    }
+
+    // If the previous session has been already restored, then we ensure that
+    // all the downloads are loaded.  Otherwise, we only ensure that the active
+    // downloads from the previous session are loaded.
+    DownloadsCommon.data.ensurePersistentDataLoaded(!this._recoverAllDownloads);
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Module
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsStartup]);
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/src/DownloadsUI.js
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This component implements the nsIDownloadManagerUI interface and opens the
+ * downloads panel in the most recent browser window when requested.
+ *
+ * If a specific preference is set, this component transparently forwards all
+ * calls to the original implementation in Toolkit, that shows the window UI.
+ */
+
+"use strict";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue",
+                                   "@mozilla.org/browser/browserglue;1",
+                                   "nsIBrowserGlue");
+
+////////////////////////////////////////////////////////////////////////////////
+//// DownloadsUI
+
+function DownloadsUI()
+{
+  XPCOMUtils.defineLazyGetter(this, "_toolkitUI", function () {
+    // Create Toolkit's nsIDownloadManagerUI implementation.
+    return Components.classesByID["{7dfdf0d1-aff6-4a34-bad1-d0fe74601642}"]
+                     .getService(Ci.nsIDownloadManagerUI);
+  });
+}
+
+DownloadsUI.prototype = {
+  classID: Components.ID("{4d99321e-d156-455b-81f7-e7aa2308134f}"),
+
+  _xpcom_factory: XPCOMUtils.generateSingletonFactory(DownloadsUI),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIDownloadManagerUI
+
+  show: function DUI_show(aWindowContext, aID, aReason)
+  {
+    if (DownloadsCommon.useToolkitUI) {
+      this._toolkitUI.show(aWindowContext, aID, aReason);
+      return;
+    }
+
+    if (!aReason) {
+      aReason = Ci.nsIDownloadManagerUI.REASON_USER_INTERACTED;
+    }
+
+    if (aReason == Ci.nsIDownloadManagerUI.REASON_NEW_DOWNLOAD) {
+      // New download notifications are already handled by the panel service.
+      // We don't handle them here because we don't want them to depend on the
+      // "browser.download.manager.showWhenStarting" and
+      // "browser.download.manager.focusWhenStarting" preferences.
+      return;
+    }
+
+    // Show the panel in the most recent browser window, if present.
+    let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
+    if (browserWin) {
+      browserWin.focus();
+      browserWin.DownloadsPanel.showPanel();
+      return;
+    }
+
+    // If no browser window is visible and the user requested to show the
+    // current downloads, try and open a new window.  We'll open the panel when
+    // delayed loading is finished.
+    Services.obs.addObserver(function DUIO_observe(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(DUIO_observe, aTopic);
+      aSubject.DownloadsPanel.showPanel();
+    }, "browser-delayed-startup-finished", false);
+
+    // We must really build an empty arguments list for the new window.
+    let windowFirstArg = Cc["@mozilla.org/supports-string;1"]
+                         .createInstance(Ci.nsISupportsString);
+    let windowArgs = Cc["@mozilla.org/supports-array;1"]
+                     .createInstance(Ci.nsISupportsArray);
+    windowArgs.AppendElement(windowFirstArg);
+    Services.ww.openWindow(null, "chrome://browser/content/browser.xul",
+                           null, "chrome,dialog=no,all", windowArgs);
+  },
+
+  get visible()
+  {
+    if (DownloadsCommon.useToolkitUI) {
+      return this._toolkitUI.visible;
+    }
+
+    let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
+    return browserWin ? browserWin.DownloadsPanel.isPanelShowing : false;
+  },
+
+  getAttention: function DUI_getAttention()
+  {
+    if (DownloadsCommon.useToolkitUI) {
+      this._toolkitUI.getAttention();
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Module
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([DownloadsUI]);
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/src/Makefile.in
@@ -0,0 +1,22 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = ../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_COMPONENTS = \
+  BrowserDownloads.manifest \
+  DownloadsStartup.js \
+  DownloadsUI.js \
+  $(NULL)
+
+EXTRA_JS_MODULES = \
+  DownloadsCommon.jsm \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/Makefile.in
@@ -0,0 +1,17 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+DEPTH     = ../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+relativesrcdir = browser/components/downloads/test
+
+include $(DEPTH)/config/autoconf.mk
+
+XPCSHELL_TESTS = unit
+
+DIRS = browser
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/Makefile.in
@@ -0,0 +1,20 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+DEPTH     = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+relativesrcdir = browser/components/downloads/test/browser
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_FILES = \
+  browser_basic_functionality.js \
+  head.js \
+  $(NULL)
+
+libs:: $(_BROWSER_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure the downloads panel can display items in the right order and
+ * contains the expected data.
+ */
+function gen_test()
+{
+  // Display one of each download state.
+  const DownloadData = [
+    { endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
+    { endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
+    { endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
+    { endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
+    { endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
+    { endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
+    { endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
+    { endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
+    { endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
+    { endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
+    { endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
+  ];
+
+  try {
+    // Ensure that state is reset in case previous tests didn't finish.
+    for (let yy in gen_resetState()) yield;
+
+    // Populate the downloads database with the data required by this test.
+    for (let yy in gen_addDownloadRows(DownloadData)) yield;
+
+    // Open the user interface and wait for data to be fully loaded.
+    for (let yy in gen_openPanel()) yield;
+
+    // Test item data and count.  This also tests the ordering of the display.
+    let richlistbox = document.getElementById("downloadsListBox");
+    is(richlistbox.children.length, DownloadData.length,
+       "There is the correct number of richlistitems");
+    for (let i = 0; i < richlistbox.children.length; i++) {
+      let element = richlistbox.children[i];
+      let dataItem = new DownloadsViewItemController(element).dataItem;
+      is(dataItem.target, DownloadData[i].name, "Download names match up");
+      is(dataItem.state, DownloadData[i].state, "Download states match up");
+      is(dataItem.file, DownloadData[i].target, "Download targets match up");
+      is(dataItem.uri, DownloadData[i].source, "Download sources match up");
+    }
+  } finally {
+    // Clean up when the test finishes.
+    for (let yy in gen_resetState()) yield;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/head.js
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Provides infrastructure for automated download components tests.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+
+const nsIDM = Ci.nsIDownloadManager;
+
+let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
+gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+registerCleanupFunction(function () {
+  gTestTargetFile.remove(false);
+});
+
+/**
+ * This objects contains a property for each column in the downloads table.
+ */
+let gDownloadRowTemplate = {
+  name: "test-download.txt",
+  source: "http://www.example.com/test-download.txt",
+  target: NetUtil.newURI(gTestTargetFile).spec,
+  startTime: 1180493839859230,
+  endTime: 1180493839859234,
+  state: nsIDM.DOWNLOAD_FINISHED,
+  currBytes: 0,
+  maxBytes: -1,
+  preferredAction: 0,
+  autoResume: 0
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Infrastructure
+
+// All test are run through the test runner.
+function test()
+{
+  testRunner.runTest(this.gen_test);
+}
+
+/**
+ * Runs a browser-chrome test defined through a generator function.
+ *
+ * This object is a singleton, initialized automatically when this script is
+ * included.  Every browser-chrome test file includes a new copy of this object.
+ */
+var testRunner = {
+  _testIterator: null,
+  _lastEventResult: undefined,
+  _testRunning: false,
+  _eventRaised: false,
+
+  // --- Main test runner ---
+
+  /**
+   * Runs the test described by the provided generator function asynchronously.
+   *
+   * Calling yield in the generator will cause it to wait until continueTest is
+   * called.  The parameter provided to continueTest will be the return value of
+   * the yield operator.
+   *
+   * @param aGenerator
+   *        Test generator function.  The function will be called with no
+   *        arguments to retrieve its iterator.
+   */
+  runTest: function TR_runTest(aGenerator) {
+    waitForExplicitFinish();
+    testRunner._testIterator = aGenerator();
+    testRunner.continueTest();
+  },
+
+  /**
+   * Continues the currently running test.
+   *
+   * @param aEventResult
+   *        This will be the return value of the yield operator in the test.
+   */
+  continueTest: function TR_continueTest(aEventResult) {
+    // Store the last event result, or set it to undefined.
+    testRunner._lastEventResult = aEventResult;
+
+    // Never reenter the main loop, but notify that the event has been raised.
+    if (testRunner._testRunning) {
+      testRunner._eventRaised = true;
+      return;
+    }
+
+    // Enter the main iteration loop.
+    testRunner._testRunning = true;
+    try {
+      do {
+        // Call the iterator, but don't leave the loop if the expected event is
+        // raised during the execution of the generator.
+        testRunner._eventRaised = false;
+        testRunner._testIterator.send(testRunner._lastEventResult);
+      } while (testRunner._eventRaised);
+    }
+    catch (e) {
+      // This block catches exceptions raised by the generator, including the
+      // normal StopIteration exception.  Unexpected exceptions are reported as
+      // test failures.
+      if (!(e instanceof StopIteration))
+        ok(false, e);
+      // In any case, stop the tests in this file.
+      finish();
+    }
+
+    // Wait for the next event or finish.
+    testRunner._testRunning = false;
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Asynchronous generator-based support subroutines
+
+//
+// The following functions are all generators that can be used inside the main
+// test generator to perform specific tasks asynchronously.  To invoke these
+// subroutines correctly, an iteration syntax should be used:
+//
+//   for (let yy in gen_example("Parameter")) yield;
+//
+
+function gen_resetState()
+{
+  let statement = Services.downloads.DBConnection.createAsyncStatement(
+                  "DELETE FROM moz_downloads");
+  try {
+    statement.executeAsync({
+      handleResult: function(aResultSet) { },
+      handleError: function(aError)
+      {
+        Cu.reportError(aError);
+      },
+      handleCompletion: function(aReason)
+      {
+        testRunner.continueTest();
+      }
+    });
+    yield;
+  } finally {
+    statement.finalize();
+  }
+
+  // Ensure that the panel is closed and data is unloaded.
+  DownloadsCommon.data.clear();
+  DownloadsCommon.data._loadState = DownloadsCommon.data.kLoadNone;
+
+  // Wait for focus on the main window.
+  waitForFocus(testRunner.continueTest);
+  yield;
+}
+
+function gen_addDownloadRows(aDataRows)
+{
+  let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
+  let parameterNames = Object.keys(gDownloadRowTemplate)
+                             .map(function(n) ":" + n)
+                             .join(", ");
+  let statement = Services.downloads.DBConnection.createAsyncStatement(
+                  "INSERT INTO moz_downloads (" + columnNames +
+                                    ") VALUES(" + parameterNames + ")");
+  try {
+    // Execute the statement for each of the provided downloads in reverse.
+    for (let i = aDataRows.length - 1; i >= 0; i--) {
+      let dataRow = aDataRows[i];
+
+      // Populate insert parameters from the provided data.
+      for (let columnName in gDownloadRowTemplate) {
+        if (!(columnName in dataRow)) {
+          // Update the provided row object with data from the global template,
+          // for columns whose value is not provided explicitly.
+          dataRow[columnName] = gDownloadRowTemplate[columnName];
+        }
+        statement.params[columnName] = dataRow[columnName];
+      }
+
+      // Run the statement asynchronously and wait.
+      statement.executeAsync({
+        handleResult: function(aResultSet) { },
+        handleError: function(aError)
+        {
+          Cu.reportError(aError);
+        },
+        handleCompletion: function(aReason)
+        {
+          testRunner.continueTest();
+        }
+      });
+      yield;
+
+      // At each iteration, ensure that the end time in the global template is
+      // distinct, as this column is used to sort each download in its category.
+      gDownloadRowTemplate.endTime++;
+    }
+  } finally {
+    statement.finalize();
+  }
+}
+
+function gen_openPanel(aData)
+{
+  // Hook to wait until the test data has been loaded.
+  let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
+  DownloadsPanel.onViewLoadCompleted = function () {
+    DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
+    originalOnViewLoadCompleted.apply(this);
+    testRunner.continueTest();
+  };
+
+  // Start loading all the downloads from the database asynchronously.
+  DownloadsCommon.data.ensurePersistentDataLoaded(false);
+
+  // Wait for focus on the main window.
+  waitForFocus(testRunner.continueTest);
+  yield;
+
+  // Open the downloads panel, waiting until loading is completed.
+  DownloadsPanel.showPanel();
+  yield;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/unit/head.js
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Provides infrastructure for automated download components tests.
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource:///modules/DownloadsCommon.jsm");
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/unit/test_DownloadsCommon.js
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests for the functions located directly in the "DownloadsCommon" object.
+ */
+
+function testFormatTimeLeft(aSeconds, aExpectedValue, aExpectedUnitString)
+{
+  let expected = "";
+  if (aExpectedValue) {
+    // Format the expected result based on the current language.
+    expected = DownloadsCommon.strings[aExpectedUnitString](aExpectedValue);
+  }
+  do_check_eq(DownloadsCommon.formatTimeLeft(aSeconds), expected);
+}
+
+function run_test()
+{
+  testFormatTimeLeft(      0,   "", "");
+  testFormatTimeLeft(      1,  "1", "shortTimeLeftSeconds");
+  testFormatTimeLeft(     29, "29", "shortTimeLeftSeconds");
+  testFormatTimeLeft(     30, "30", "shortTimeLeftSeconds");
+  testFormatTimeLeft(     31,  "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(     60,  "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(     89,  "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(     90,  "2", "shortTimeLeftMinutes");
+  testFormatTimeLeft(     91,  "2", "shortTimeLeftMinutes");
+  testFormatTimeLeft(   3600,  "1", "shortTimeLeftHours");
+  testFormatTimeLeft(  86400, "24", "shortTimeLeftHours");
+  testFormatTimeLeft( 169200, "47", "shortTimeLeftHours");
+  testFormatTimeLeft( 172800,  "2", "shortTimeLeftDays");
+  testFormatTimeLeft(8553600, "99", "shortTimeLeftDays");
+  testFormatTimeLeft(8640000, "99", "shortTimeLeftDays");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/unit/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head = head.js
+tail =
+
+[test_DownloadsCommon.js]
--- a/browser/components/feeds/src/BrowserFeeds.manifest
+++ b/browser/components/feeds/src/BrowserFeeds.manifest
@@ -1,16 +1,29 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {229fa115-9412-4d32-baf3-2fc407f76fb1} FeedConverter.js
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1}
 component {2376201c-bbc6-472f-9b62-7548040a61c6} FeedConverter.js
 contract @mozilla.org/browser/feeds/result-service;1 {2376201c-bbc6-472f-9b62-7548040a61c6}
 component {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} FeedConverter.js
 contract @mozilla.org/network/protocol;1?name=feed {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}
 component {1c31ed79-accd-4b94-b517-06e0c81999d5} FeedConverter.js
 contract @mozilla.org/network/protocol;1?name=pcast {1c31ed79-accd-4b94-b517-06e0c81999d5}
 component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js
 contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e}
 category JavaScript-global-constructor BrowserFeedWriter @mozilla.org/browser/feeds/result-writer;1
 component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js
 contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc}
-category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1
\ No newline at end of file
+category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/components/migration/src/BrowserProfileMigrators.manifest
+++ b/browser/components/migration/src/BrowserProfileMigrators.manifest
@@ -1,8 +1,10 @@
 component {6F8BB968-C14F-4D6F-9733-6C6737B35DCE} ProfileMigrator.js
 contract @mozilla.org/toolkit/profile-migrator;1 {6F8BB968-C14F-4D6F-9733-6C6737B35DCE}
 component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
 component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
+component {3d2532e3-4932-4774-b7ba-968f5899d3a4} IEProfileMigrator.js
+contract @mozilla.org/profile/migrator;1?app=browser&type=ie {3d2532e3-4932-4774-b7ba-968f5899d3a4}
 component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/src/IEProfileMigrator.js
@@ -0,0 +1,678 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
+const kRegMultiSz = 7;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource:///modules/MigrationUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
+                                  "resource://gre/modules/ctypes.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+//// Helpers.
+
+let CtypesHelpers = {
+  _structs: {},
+  _functions: {},
+  _libs: {},
+
+  /**
+   * Must be invoked once before first use of any of the provided helpers.
+   */
+  initialize: function CH_initialize() {
+    const WORD = ctypes.uint16_t;
+    const DWORD = ctypes.uint32_t;
+    const BOOL = ctypes.int;
+
+    this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
+      {wYear: WORD},
+      {wMonth: WORD},
+      {wDayOfWeek: WORD},
+      {wDay: WORD},
+      {wHour: WORD},
+      {wMinute: WORD},
+      {wSecond: WORD},
+      {wMilliseconds: WORD}
+    ]);
+
+    this._structs.FILETIME = new ctypes.StructType('FILETIME', [
+      {dwLowDateTime: DWORD},
+      {dwHighDateTime: DWORD}
+    ]);
+
+    try {
+      this._libs.kernel32 = ctypes.open("Kernel32");
+      this._functions.FileTimeToSystemTime =
+        this._libs.kernel32.declare("FileTimeToSystemTime",
+                                    ctypes.default_abi,
+                                    BOOL,
+                                    this._structs.FILETIME.ptr,
+                                    this._structs.SYSTEMTIME.ptr);
+    } catch (ex) {
+      this.finalize();
+    }
+  },
+
+  /**
+   * Must be invoked once after last use of any of the provided helpers.
+   */
+  finalize: function CH_finalize() {
+    this._structs = {};
+    this._functions = {};
+    for each (let lib in this._libs) {
+      try {
+        lib.close();
+      } catch (ex) {}
+    }
+    this._libs = {};
+  },
+
+  /**
+   * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct.
+   *
+   * @param aTimeHi
+   *        Least significant DWORD.
+   * @param aTimeLo
+   *        Most significant DWORD.
+   * @return a Date object representing the converted datetime.
+   */
+  fileTimeToDate: function CH_fileTimeToDate(aTimeHi, aTimeLo) {
+    let fileTime = this._structs.FILETIME();
+    fileTime.dwLowDateTime = aTimeLo;
+    fileTime.dwHighDateTime = aTimeHi;
+    let systemTime = this._structs.SYSTEMTIME();
+    let result = this._functions.FileTimeToSystemTime(fileTime.address(),
+                                                      systemTime.address());
+    if (result == 0)
+      throw new Error(ctypes.winLastError);
+
+    return new Date(systemTime.wYear,
+                    systemTime.wMonth - 1,
+                    systemTime.wDay,
+                    systemTime.wHour,
+                    systemTime.wMinute,
+                    systemTime.wSecond,
+                    systemTime.wMilliseconds);
+  }
+};
+
+/**
+ * Checks whether an host is an IP (v4 or v6) address.
+ *
+ * @param aHost
+ *        The host to check.
+ * @return whether aHost is an IP address.
+ */
+function hostIsIPAddress(aHost) {
+  try {
+    Services.eTLD.getBaseDomainFromHost(aHost);
+  } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
+    return true;
+  } catch (e) {}
+  return false;
+}
+
+/**
+ * Safely reads a value from the registry.
+ *
+ * @param aRoot
+ *        The root registry to use.
+ * @param aPath
+ *        The registry path to the key.
+ * @param aKey
+ *        The key name.
+ * @return The key value or undefined if it doesn't exist.  If the key is
+ *         a REG_MULTI_SZ, an array is returned.
+ */
+function readRegKey(aRoot, aPath, aKey) {
+  let registry = Cc["@mozilla.org/windows-registry-key;1"].
+                 createInstance(Ci.nsIWindowsRegKey);
+  try {
+    registry.open(aRoot, aPath, Ci.nsIWindowsRegKey.ACCESS_READ);
+    if (registry.hasValue(aKey)) {
+      let type = registry.getValueType(aKey);
+      switch (type) {
+        case kRegMultiSz:
+          // nsIWindowsRegKey doesn't support REG_MULTI_SZ type out of the box.
+          let str = registry.readStringValue(aKey);
+          return [v for each (v in str.split("\0")) if (v)];
+        case Ci.nsIWindowsRegKey.TYPE_STRING:
+          return registry.readStringValue(aKey);
+        case Ci.nsIWindowsRegKey.TYPE_INT:
+          return registry.readIntValue(aKey);
+        default:
+          throw new Error("Unsupported registry value.");
+      }
+    }
+  } catch (ex) {
+  } finally {
+    registry.close();
+  }
+  return undefined;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Resources
+
+function Bookmarks() {
+}
+
+Bookmarks.prototype = {
+  type: MigrationUtils.resourceTypes.BOOKMARKS,
+
+  get exists() !!this._favoritesFolder,
+
+  __favoritesFolder: null,
+  get _favoritesFolder() {
+    if (!this.__favoritesFolder) {
+      let favoritesFolder = Services.dirsvc.get("Favs", Ci.nsIFile);
+      if (favoritesFolder.exists() && favoritesFolder.isReadable())
+        this.__favoritesFolder = favoritesFolder;
+    }
+    return this.__favoritesFolder;
+  },
+
+  __toolbarFolderName: null,
+  get _toolbarFolderName() {
+    if (!this.__toolbarFolderName) {
+      // Retrieve the name of IE's favorites subfolder that holds the bookmarks
+      // in the toolbar. This was previously stored in the registry and changed
+      // in IE7 to always be called "Links".
+      let folderName = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                  "Software\\Microsoft\\Internet Explorer\\Toolbar",
+                                  "LinksFolderName");
+      this.__toolbarFolderName = folderName || "Links";
+    }
+    return this.__toolbarFolderName;
+  },
+
+  migrate: function B_migrate(aCallback) {
+    PlacesUtils.bookmarks.runInBatchMode({
+      runBatched: (function migrateBatched() {
+        // Import to the bookmarks menu.
+        let destFolderId = PlacesUtils.bookmarksMenuFolderId;
+        if (!MigrationUtils.isStartupMigration) {
+          destFolderId =
+            MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
+        }
+
+        this._migrateFolder(this._favoritesFolder, destFolderId);
+
+        aCallback(true);
+      }).bind(this)
+    }, null);
+  },
+
+  _migrateFolder: function B__migrateFolder(aSourceFolder, aDestFolderId) {
+    // TODO (bug 741993): the favorites order is stored in the Registry, at
+    // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
+    // Until we support it, bookmarks are imported in alphabetical order.
+    let entries = aSourceFolder.directoryEntries;
+    while (entries.hasMoreElements()) {
+      let entry = entries.getNext().QueryInterface(Ci.nsIFile);
+
+      // Make sure that entry.path == entry.target to not follow .lnk folder
+      // shortcuts which could lead to infinite cycles.
+      if (entry.isDirectory() && entry.path == entry.target) {
+        let destFolderId;
+        if (entry.leafName == this._toolbarFolderName &&
+            entry.parent.equals(this._favoritesFolder)) {
+          // Import to the bookmarks toolbar.
+          destFolderId = PlacesUtils.toolbarFolderId;
+          if (!MigrationUtils.isStartupMigration) {
+            destFolderId =
+              MigrationUtils.createImportedBookmarksFolder("IE", destFolderId);
+          }
+        }
+        else {
+          // Import to a new folder.
+          destFolderId =
+            PlacesUtils.bookmarks.createFolder(aDestFolderId, entry.leafName,
+                                               PlacesUtils.bookmarks.DEFAULT_INDEX);
+        }
+
+        if (entry.isReadable()) {
+          // Recursively import the folder.
+          this._migrateFolder(entry, destFolderId);
+        }
+      }
+      else {
+        // Strip the .url extension, to both check this is a valid link file,
+        // and get the associated title.
+        let matches = entry.leafName.match(/(.+)\.url$/);
+        if (matches) {
+          let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
+                            getService(Ci.nsIFileProtocolHandler);
+          let uri = fileHandler.readURLFile(entry);
+          let title = matches[1];
+
+          PlacesUtils.bookmarks.insertBookmark(aDestFolderId,
+                                               uri,
+                                               PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                               title);
+        }
+      }
+    }
+  }
+};
+
+function History() {
+}
+
+History.prototype = {
+  type: MigrationUtils.resourceTypes.HISTORY,
+
+  get exists() true,
+
+  __typedURLs: null,
+  get _typedURLs() {
+    if (!this.__typedURLs) {
+      // The list of typed URLs is a sort of annotation stored in the registry.
+      // Currently, IE stores 25 entries and this value is not configurable,
+      // but we just keep reading up to the first non-existing entry to support
+      // possible future bumps of this limit.
+      this.__typedURLs = {};
+      let registry = Cc["@mozilla.org/windows-registry-key;1"].
+                     createInstance(Ci.nsIWindowsRegKey);
+      try {
+        registry.open(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                      "Software\\Microsoft\\Internet Explorer\\TypedURLs",
+                      Ci.nsIWindowsRegKey.ACCESS_READ);
+        for (let entry = 1; registry.hasValue("url" + entry); entry++) {
+          let url = registry.readStringValue("url" + entry);
+          this.__typedURLs[url] = true;
+        }
+      } catch (ex) {
+      } finally {
+        registry.close();
+      }
+    }
+    return this.__typedURLs;
+  },
+
+  migrate: function H_migrate(aCallback) {
+    let places = [];
+    let historyEnumerator = Cc["@mozilla.org/profile/migrator/iehistoryenumerator;1"].
+                            createInstance(Ci.nsISimpleEnumerator);
+    while (historyEnumerator.hasMoreElements()) {
+      let entry = historyEnumerator.getNext().QueryInterface(Ci.nsIPropertyBag2);
+      let uri = entry.get("uri").QueryInterface(Ci.nsIURI);
+      // MSIE stores some types of URLs in its history that we don't handle,
+      // like HTMLHelp and others.  Since we don't properly map handling for
+      // all of them we just avoid importing them.
+      if (["http", "https", "ftp", "file"].indexOf(uri.scheme) == -1) {
+        continue;
+      }
+
+      let title = entry.get("title");
+      // Embed visits have no title and don't need to be imported.
+      if (title.length == 0) {
+        continue;
+      }
+
+      // The typed urls are already fixed-up, so we can use them for comparison.
+      let transitionType = this._typedURLs[uri.spec] ?
+                             Ci.nsINavHistoryService.TRANSITION_TYPED :
+                             Ci.nsINavHistoryService.TRANSITION_LINK;
+      let lastVisitTime = entry.get("time");
+
+      places.push(
+        { uri: uri,
+          title: title,
+          visits: [{ transitionType: transitionType,
+                     visitDate: lastVisitTime }]
+        }
+      );
+    }
+
+    // Check whether there is any history to import.
+    if (places.length == 0) {
+      aCallback(true);
+      return;
+    }
+
+    PlacesUtils.asyncHistory.updatePlaces(places, {
+      _success: false,
+      handleResult: function() {
+        // Importing any entry is considered a successful import.
+        this._success = true;
+      },
+      handleError: function() {},
+      handleCompletion: function() {
+        aCallback(this._success);
+      }
+    });
+  }
+};
+
+function Cookies() {
+}
+
+Cookies.prototype = {
+  type: MigrationUtils.resourceTypes.COOKIES,
+
+  get exists() !!this._cookiesFolder,
+
+  __cookiesFolder: null,
+  get _cookiesFolder() {
+    // Cookies are stored in txt files, in a Cookies folder whose path varies
+    // across the different OS versions.  CookD takes care of most of these
+    // cases, though, in Windows Vista/7, UAC makes a difference.
+    // If UAC is enabled, the most common destination is CookD/Low.  Though,
+    // if the user runs the application in administrator mode or disables UAC,
+    // cookies are stored in the original CookD destination.  Cause running the
+    // browser in administrator mode is unsafe and discouraged, we just care
+    // about the UAC state.
+    if (!this.__cookiesFolder) {
+      let cookiesFolder = Services.dirsvc.get("CookD", Ci.nsIFile);
+      if (cookiesFolder.exists() && cookiesFolder.isReadable()) {
+        // Check if UAC is enabled.
+        if (Services.appinfo.QueryInterface(Ci.nsIWinAppHelper).userCanElevate) {
+          cookiesFolder.append("Low");
+        }
+        this.__cookiesFolder = cookiesFolder;
+      }
+    }
+    return this.__cookiesFolder;
+  },
+
+  migrate: function C_migrate(aCallback) {
+    CtypesHelpers.initialize();
+
+    let cookiesGenerator = (function genCookie() {
+      let success = false;
+      let entries = this._cookiesFolder.directoryEntries;
+      while (entries.hasMoreElements()) {
+        let entry = entries.getNext().QueryInterface(Ci.nsIFile);
+        // Skip eventual bogus entries.
+        if (!entry.isFile() || !/\.txt$/.test(entry.leafName))
+          continue;
+
+        this._readCookieFile(entry, function(aSuccess) {
+          // Importing even a single cookie file is considered a success.
+          if (aSuccess)
+            success = true;
+          try {
+            cookiesGenerator.next();
+          } catch (ex) {}
+        });
+
+        yield;
+      }
+
+      CtypesHelpers.finalize();
+
+      aCallback(success);
+    }).apply(this);
+    cookiesGenerator.next();
+  },
+
+  _readCookieFile: function C__readCookieFile(aFile, aCallback) {
+    let fileReader = Cc["@mozilla.org/files/filereader;1"].
+                     createInstance(Ci.nsIDOMFileReader);
+    fileReader.addEventListener("loadend", (function onLoadEnd() {
+      fileReader.removeEventListener("loadend", onLoadEnd, false);
+
+      if (fileReader.readyState != fileReader.DONE) {
+        Cu.reportError("Could not read cookie contents: " + fileReader.error);
+        aCallback(false);
+        return;
+      }
+
+      let success = true;
+      try {
+        this._parseCookieBuffer(fileReader.result);
+      } catch (ex) {
+        Components.utils.reportError("Unable to migrate cookie: " + ex);
+        success = false;
+      } finally {
+        aCallback(success);
+      }
+    }).bind(this), false);
+    fileReader.readAsText(File(aFile));
+  },
+
+  /**
+   * Parses a cookie file buffer and returns an array of the contained cookies.
+   *
+   * The cookie file format is a newline-separated-values with a "*" used as
+   * delimeter between multiple records.
+   * Each cookie has the following fields:
+   *  - name
+   *  - value
+   *  - host/path
+   *  - flags
+   *  - Expiration time most significant integer
+   *  - Expiration time least significant integer
+   *  - Creation time most significant integer
+   *  - Creation time least significant integer
+   *  - Record delimiter "*"
+   *
+   * @note All the times are in FILETIME format.
+   */
+  _parseCookieBuffer: function C__parseCookieBuffer(aTextBuffer) {
+    // Note the last record is an empty string.
+    let records = [r for each (r in aTextBuffer.split("*\n")) if (r)];
+    for (let record of records) {
+      let [name, value, hostpath, flags,
+           expireTimeLo, expireTimeHi] = record.split("\n");
+
+      // IE stores deleted cookies with a zero-length value, skip them.
+      if (value.length == 0)
+        continue;
+
+      let hostLen = hostpath.indexOf("/");
+      let host = hostpath.substr(0, hostLen);
+
+      // For a non-null domain, assume it's what Mozilla considers
+      // a domain cookie.  See bug 222343.
+      if (host.length > 0) {
+        // Fist delete any possible extant matching host cookie.
+        Services.cookies.remove(host, name, path, false);
+        // Now make it a domain cookie.
+        if (host[0] != "." && !hostIsIPAddress(host))
+          host = "." + host;
+      }
+
+      let path = hostpath.substr(hostLen);
+      let expireTime = CtypesHelpers.fileTimeToDate(Number(expireTimeHi),
+                                                    Number(expireTimeLo));
+      Services.cookies.add(host,
+                           path,
+                           name,
+                           value,
+                           Number(flags) & 0x1, // secure
+                           false, // httpOnly
+                           false, // session
+                           expireTime);
+    }
+  }
+};
+
+function Settings() {
+}
+
+Settings.prototype = {
+  type: MigrationUtils.resourceTypes.SETTINGS,
+
+  get exists() true,
+
+  migrate: function S_migrate(aCallback) {
+    // Converts from yes/no to a boolean.
+    function yesNoToBoolean(v) v == "yes";
+
+    // Converts source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
+    // destination format like "en-us, ar-kw, ar-om".
+    // Final string is sorted by quality (q=) param.
+    function parseAcceptLanguageList(v) {
+      return v.match(/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/gi)
+              .sort(function (a , b) {
+                let qA = parseFloat(a.split(";q=")[1]) || 1.0;
+                let qB = parseFloat(b.split(";q=")[1]) || 1.0;
+                return qA < qB ? 1 : qA == qB ? 0 : -1;
+              })
+              .map(function(a) a.split(";")[0]);
+    }
+
+    // For reference on some of the available IE Registry settings:
+    //  * http://msdn.microsoft.com/en-us/library/cc980058%28v=prot.13%29.aspx
+    //  * http://msdn.microsoft.com/en-us/library/cc980059%28v=prot.13%29.aspx
+
+    // Note that only settings exposed in our UI should be migrated.
+
+    this._set("Software\\Microsoft\\Internet Explorer\\International",
+              "AcceptLanguage",
+              "intl.accept_languages",
+              parseAcceptLanguageList);
+    // TODO (bug 745853): For now, only x-western font is translated.
+    this._set("Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
+              "IEFixedFontName",
+              "font.name.monospace.x-western");
+    this._set(kMainKey,
+              "Use FormSuggest",
+              "browser.formfill.enable",
+              yesNoToBoolean);
+    this._set(kMainKey,
+              "FormSuggest Passwords",
+              "signon.rememberSignons",
+              yesNoToBoolean);
+    this._set(kMainKey,
+              "Anchor Underline",
+              "browser.underline_anchors",
+              yesNoToBoolean);
+    this._set(kMainKey,
+              "Display Inline Images",
+              "permissions.default.image",
+              function (v) yesNoToBoolean(v) ? 1 : 2);
+    this._set(kMainKey,
+              "Move System Caret",
+              "accessibility.browsewithcaret",
+              yesNoToBoolean);
+    this._set("Software\\Microsoft\\Internet Explorer\\Settings",
+              "Always Use My Colors",
+              "browser.display.use_document_colors",
+              function (v) !Boolean(v));
+    this._set("Software\\Microsoft\\Internet Explorer\\Settings",
+              "Always Use My Font Face",
+              "browser.display.use_document_fonts",
+              function (v) !Boolean(v));
+    this._set(kMainKey,
+              "SmoothScroll",
+              "general.smoothScroll",
+              Boolean);
+    this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
+              "WarnOnClose",
+              "browser.tabs.warnOnClose",
+              Boolean);
+    this._set("Software\\Microsoft\\Internet Explorer\\TabbedBrowsing\\",
+              "OpenInForeground",
+              "browser.tabs.loadInBackground",
+              function (v) !Boolean(v));
+
+    aCallback(true);
+  },
+
+  /**
+   * Reads a setting from the Registry and stores the converted result into
+   * the appropriate Firefox preference.
+   * 
+   * @param aPath
+   *        Registry path under HKCU.
+   * @param aKey
+   *        Name of the key.
+   * @param aPref
+   *        Firefox preference.
+   * @param [optional] aTransformFn
+   *        Conversion function from the Registry format to the pref format.
+   */
+  _set: function S__set(aPath, aKey, aPref, aTransformFn) {
+    let value = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                           aPath, aKey);
+    // Don't import settings that have never been flipped.
+    if (value === undefined)
+      return;
+
+    if (aTransformFn)
+      value = aTransformFn(value);
+
+    switch (typeof(value)) {
+      case "string":
+        Services.prefs.setCharPref(aPref, value);
+        break;
+      case "number":
+        Services.prefs.setIntPref(aPref, value);
+        break;
+      case "boolean":
+        Services.prefs.setBoolPref(aPref, value);
+        break;
+      default:
+        throw new Error("Unexpected value type: " + typeof(value));
+    }
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Migrator
+
+function IEProfileMigrator()
+{
+}
+
+IEProfileMigrator.prototype = Object.create(MigratorPrototype);
+
+IEProfileMigrator.prototype.getResources = function IE_getResources() {
+  let resources = [
+    new Bookmarks()
+  , new History()
+  , new Cookies()
+  , new Settings()
+  ];
+  return [r for each (r in resources) if (r.exists)];
+};
+
+Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
+  get: function IE_get_sourceHomePageURL() {
+    let defaultStartPage = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+                                      kMainKey, "Default_Page_URL");
+    let startPage = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                               kMainKey, "Start Page");
+    // If the user didn't customize the Start Page, he is still on the default
+    // page, that may be considered the equivalent of our about:home.  There's
+    // no reason to retain it, since it is heavily targeted to IE.
+    let homepage = startPage != defaultStartPage ? startPage : "";
+
+    // IE7+ supports secondary home pages located in a REG_MULTI_SZ key.  These
+    // are in addition to the Start Page, and no empty entries are possible,
+    // thus a Start Page is always defined if any of these exists, though it
+    // may be the default one.
+    let secondaryPages = readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                    kMainKey, "Secondary Start Pages");
+    if (secondaryPages) {
+      if (homepage)
+        secondaryPages.unshift(homepage);
+      homepage = secondaryPages.join("|");
+    }
+
+    return homepage;
+  }
+});
+
+IEProfileMigrator.prototype.classDescription = "IE Profile Migrator";
+IEProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=ie";
+IEProfileMigrator.prototype.classID = Components.ID("{3d2532e3-4932-4774-b7ba-968f5899d3a4}");
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([IEProfileMigrator]);
--- a/browser/components/migration/src/Makefile.in
+++ b/browser/components/migration/src/Makefile.in
@@ -50,22 +50,20 @@ endif
 
 EXTRA_PP_COMPONENTS = \
   ProfileMigrator.js \
   ChromeProfileMigrator.js \
   FirefoxProfileMigrator.js \
   $(NULL)
 
 ifeq ($(OS_ARCH)_$(GNU_CXX),WINNT_)
-CPPSRCS += nsIEProfileMigrator.cpp \
-           nsBrowserProfileMigratorUtils.cpp \
-           nsIEHistoryEnumerator.cpp \
-           $(NULL)
+CPPSRCS += nsIEHistoryEnumerator.cpp
 
-EXTRA_PP_COMPONENTS += SafariProfileMigrator.js \
+EXTRA_PP_COMPONENTS += IEProfileMigrator.js \
+                       SafariProfileMigrator.js \
                        $(NULL)
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_PP_COMPONENTS += SafariProfileMigrator.js \
                        $(NULL)
 endif
 
deleted file mode 100644
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is The Browser Profile Migrator.
- *
- * The Initial Developer of the Original Code is Ben Goodger.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Ben Goodger <ben@bengoodger.com>
- *  Asaf Romano <mozilla.mano@sent.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsBrowserProfileMigratorUtils.h"
-#include "nsINavBookmarksService.h"
-#include "nsBrowserCompsCID.h"
-#include "nsToolkitCompsCID.h"
-#include "nsIPlacesImportExportService.h"
-#include "nsIFile.h"
-#include "nsIProperties.h"
-#include "nsIProfileMigrator.h"
-
-#include "nsIURI.h"
-#include "nsNetUtil.h"
-#include "nsISupportsPrimitives.h"
-
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsIRDFService.h"
-#include "nsIStringBundle.h"
-#include "nsXPCOMCID.h"
-
-void SetUnicharPref(const char* aPref, const nsAString& aValue,
-                    nsIPrefBranch* aPrefs)
-{
-  nsCOMPtr<nsISupportsString> supportsString =
-    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);    
-  if (supportsString) {
-     supportsString->SetData(aValue); 
-     aPrefs->SetComplexValue(aPref, NS_GET_IID(nsISupportsString),
-                             supportsString);
-  }
-}
-
-void SetProxyPref(const nsAString& aHostPort, const char* aPref, 
-                  const char* aPortPref, nsIPrefBranch* aPrefs) 
-{
-  nsCOMPtr<nsIURI> uri;
-  nsCAutoString host;
-  PRInt32 portValue;
-
-  // try parsing it as a URI first
-  if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), aHostPort))
-      && NS_SUCCEEDED(uri->GetHost(host))
-      && !host.IsEmpty()
-      && NS_SUCCEEDED(uri->GetPort(&portValue))) {
-    SetUnicharPref(aPref, NS_ConvertUTF8toUTF16(host), aPrefs);
-    aPrefs->SetIntPref(aPortPref, portValue);
-  }
-  else {
-    nsAutoString hostPort(aHostPort);  
-    PRInt32 portDelimOffset = hostPort.RFindChar(':');
-    if (portDelimOffset > 0) {
-      SetUnicharPref(aPref, Substring(hostPort, 0, portDelimOffset), aPrefs);
-      nsAutoString port(Substring(hostPort, portDelimOffset + 1));
-      nsresult stringErr;
-      portValue = port.ToInteger(&stringErr);
-      if (NS_SUCCEEDED(stringErr))
-        aPrefs->SetIntPref(aPortPref, portValue);
-    }
-    else
-      SetUnicharPref(aPref, hostPort, aPrefs); 
-  }
-}
-
-void ParseOverrideServers(const nsAString& aServers, nsIPrefBranch* aBranch)
-{
-  // Windows (and Opera) formats its proxy override list in the form:
-  // server;server;server where server is a server name or ip address, 
-  // or "<local>". Mozilla's format is server,server,server, and <local>
-  // must be translated to "localhost,127.0.0.1"
-  nsAutoString override(aServers);
-  PRInt32 left = 0, right = 0;
-  for (;;) {
-    right = override.FindChar(';', right);
-    const nsAString& host = Substring(override, left, 
-                                      (right < 0 ? override.Length() : right) - left);
-    if (host.EqualsLiteral("<local>"))
-      override.Replace(left, 7, NS_LITERAL_STRING("localhost,127.0.0.1"));
-    if (right < 0)
-      break;
-    left = right + 1;
-    override.Replace(right, 1, NS_LITERAL_STRING(","));
-  }
-  SetUnicharPref("network.proxy.no_proxies_on", override, aBranch); 
-}
-
-void GetMigrateDataFromArray(MigrationData* aDataArray, PRInt32 aDataArrayLength, 
-                             bool aReplace, nsIFile* aSourceProfile, 
-                             PRUint16* aResult)
-{
-  nsCOMPtr<nsIFile> sourceFile; 
-  bool exists;
-  MigrationData* cursor;
-  MigrationData* end = aDataArray + aDataArrayLength;
-  for (cursor = aDataArray; cursor < end && cursor->fileName; ++cursor) {
-    // When in replace mode, all items can be imported. 
-    // When in non-replace mode, only items that do not require file replacement
-    // can be imported.
-    if (aReplace || !cursor->replaceOnly) {
-      aSourceProfile->Clone(getter_AddRefs(sourceFile));
-      sourceFile->Append(nsDependentString(cursor->fileName));
-      sourceFile->Exists(&exists);
-      if (exists)
-        *aResult |= cursor->sourceFlag;
-    }
-    NS_Free(cursor->fileName);
-    cursor->fileName = nsnull;
-  }
-}
-
-void
-GetProfilePath(nsIProfileStartup* aStartup, nsCOMPtr<nsIFile>& aProfileDir)
-{
-  if (aStartup) {
-    aStartup->GetDirectory(getter_AddRefs(aProfileDir));
-  }
-  else {
-    nsCOMPtr<nsIProperties> dirSvc
-      (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
-    if (dirSvc) {
-      dirSvc->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
-                  (void**) getter_AddRefs(aProfileDir));
-    }
-  }
-}
-
deleted file mode 100644
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is The Browser Profile Migrator.
- *
- * The Initial Developer of the Original Code is Ben Goodger.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Ben Goodger <ben@bengoodger.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef browserprofilemigratorutils___h___
-#define browserprofilemigratorutils___h___
-
-#define MIGRATION_ITEMBEFOREMIGRATE "Migration:ItemBeforeMigrate"
-#define MIGRATION_ITEMMIGRATEERROR  "Migration:ItemError"
-#define MIGRATION_ITEMAFTERMIGRATE  "Migration:ItemAfterMigrate"
-#define MIGRATION_STARTED           "Migration:Started"
-#define MIGRATION_ENDED             "Migration:Ended"
-
-#define NOTIFY_OBSERVERS(message, item) \
-  mObserverService->NotifyObservers(nsnull, message, item)
-
-#define COPY_DATA(func, replace, itemIndex) \
-  if ((aItems & itemIndex || !aItems)) { \
-    nsAutoString index; \
-    index.AppendInt(itemIndex); \
-    NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get()); \
-    if (NS_FAILED(func(replace))) \
-      NOTIFY_OBSERVERS(MIGRATION_ITEMMIGRATEERROR, index.get()); \
-    NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get()); \
-  }
-
-#define NC_URI(property) \
-  NS_LITERAL_CSTRING("http://home.netscape.com/NC-rdf#"#property)
-
-#define BATCH_ACTION_HISTORY 0
-#define BATCH_ACTION_HISTORY_REPLACE 1
-#define BATCH_ACTION_BOOKMARKS 2
-#define BATCH_ACTION_BOOKMARKS_REPLACE 3
-
-#include "nsIPrefBranch.h"
-#include "nsIFile.h"
-#include "nsStringAPI.h"
-#include "nsCOMPtr.h"
-
-class nsIProfileStartup;
-
-
-void SetUnicharPref(const char* aPref, const nsAString& aValue,
-                    nsIPrefBranch* aPrefs);
-
-// Proxy utilities shared by the Opera and IE migrators
-void ParseOverrideServers(const nsAString& aServers, nsIPrefBranch* aBranch);
-void SetProxyPref(const nsAString& aHostPort, const char* aPref, 
-                  const char* aPortPref, nsIPrefBranch* aPrefs);
-
-struct MigrationData { 
-  PRUnichar* fileName; 
-  PRUint32 sourceFlag;
-  bool replaceOnly;
-};
-
-class nsILocalFile;
-void GetMigrateDataFromArray(MigrationData* aDataArray, 
-                             PRInt32 aDataArrayLength,
-                             bool aReplace,
-                             nsIFile* aSourceProfile, 
-                             PRUint16* aResult);
-
-
-// get the base directory of the *target* profile
-// this is already cloned, modify it to your heart's content
-void GetProfilePath(nsIProfileStartup* aStartup, nsCOMPtr<nsIFile>& aProfileDir);
-
-#endif
-
deleted file mode 100644
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ /dev/null
@@ -1,2209 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Matejka
- *     (Original Author)
- *   Ben Goodger <ben@bengoodger.com> 
- *     (History, Favorites, Passwords, Form Data, some settings)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "mozilla/Util.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <windows.h>
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsBrowserProfileMigratorUtils.h"
-#include "nsCOMPtr.h"
-#include "nsCRTGlue.h"
-#include "nsNetCID.h"
-#include "nsDocShellCID.h"
-#include "nsDebug.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsStringAPI.h"
-#include "plstr.h"
-#include "prio.h"
-#include "prmem.h"
-#include "prlong.h"
-#include "nsICookieManager2.h"
-#include "nsIEProfileMigrator.h"
-#include "nsIFile.h"
-#include "nsILocalFile.h"
-#include "nsIPrefService.h"
-#include "nsIPrefBranch.h"
-#include "nsISimpleEnumerator.h"
-#include "nsIProfileMigrator.h"
-#include "nsIBrowserProfileMigrator.h"
-#include "nsIObserverService.h"
-#include "nsILocalFileWin.h"
-#include "nsAutoPtr.h"
-
-#include "prnetdb.h"
-
-#include <objbase.h>
-#include <shlguid.h>
-#include <urlhist.h>
-#include <comdef.h>
-#include <shlobj.h>
-#include <intshcut.h>
-
-#include "nsIBrowserHistory.h"
-#include "nsIGlobalHistory.h"
-#include "nsIRDFRemoteDataSource.h"
-#include "nsIURI.h"
-#include "nsILoginManagerIEMigrationHelper.h"
-#include "nsILoginInfo.h"
-#include "nsIFormHistory.h"
-#include "nsIRDFService.h"
-#include "nsIRDFContainer.h"
-#include "nsIURL.h"
-#include "nsINavBookmarksService.h"
-#include "nsBrowserCompsCID.h"
-#include "nsIStringBundle.h"
-#include "nsNetUtil.h"
-#include "nsToolkitCompsCID.h"
-#include "nsUnicharUtils.h"
-#include "nsIWindowsRegKey.h"
-#include "nsISupportsPrimitives.h"
-#include "jsapi.h"
-
-#define kNotFound -1
-
-#define TRIDENTPROFILE_BUNDLE       "chrome://browser/locale/migration/migration.properties"
-
-#define REGISTRY_IE_MAIN_KEY \
-  NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\Main")
-#define REGISTRY_IE_TYPEDURL_KEY \
-  NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\TypedURLs")
-#define REGISTRY_IE_TOOLBAR_KEY \
-  NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\Toolbar")
-#define REGISTRY_IE_SEARCHURL_KEY \
-  NS_LITERAL_STRING("Software\\Microsoft\\Internet Explorer\\SearchUrl")
-
-using namespace mozilla;
-
-const int sInitialCookieBufferSize = 1024; // but it can grow
-const int sUsernameLengthLimit     = 80;
-const int sHostnameLengthLimit     = 255;
-
-//***********************************************************************
-//*** Replacements for comsupp.lib calls used by pstorec.dll
-//***********************************************************************
-void  __stdcall _com_issue_error(HRESULT hr)
-{
-  // XXX - Do nothing for now
-}
-
-//***********************************************************************
-//*** windows registry to mozilla prefs data type translation functions
-//***********************************************************************
-
-typedef void (*regEntryHandler)(nsIWindowsRegKey *, const nsString&,
-                                nsIPrefBranch *, char *);
-
-// yes/no string to T/F boolean
-void
-TranslateYNtoTF(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  // input type is a string, lowercase "yes" or "no"
-  nsAutoString regValue; 
-  if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)))
-    aPrefs->SetBoolPref(aPrefKeyName, regValue.EqualsLiteral("yes"));
-}
-
-// yes/no string to F/T boolean
-void
-TranslateYNtoFT(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  // input type is a string, lowercase "yes" or "no"
-  nsAutoString regValue; 
-  if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)))
-    aPrefs->SetBoolPref(aPrefKeyName, !regValue.EqualsLiteral("yes"));
-}
-
-void
-TranslateYNtoImageBehavior(nsIWindowsRegKey *aRegKey,
-                           const nsString& aRegValueName,
-                           nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-  // input type is a string, lowercase "yes" or "no"
-  nsAutoString regValue; 
-  if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
-      !regValue.IsEmpty()) {
-    if (regValue.EqualsLiteral("yes"))
-      aPrefs->SetIntPref(aPrefKeyName, 1);
-    else
-      aPrefs->SetIntPref(aPrefKeyName, 2);
-  }
-}
-
-void
-TranslateDWORDtoHTTPVersion(nsIWindowsRegKey *aRegKey,
-                            const nsString& aRegValueName,
-                            nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-  PRUint32 val;
-  if (NS_SUCCEEDED(aRegKey->ReadIntValue(aRegValueName, &val))) {
-    if (val & 0x1) 
-      aPrefs->SetCharPref(aPrefKeyName, "1.1");
-    else
-      aPrefs->SetCharPref(aPrefKeyName, "1.0");
-  }
-}
-
-// decimal RGB (1,2,3) to hex RGB (#010203)
-void
-TranslateDRGBtoHRGB(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                    nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  // clear previous settings with defaults
-  char prefStringValue[10];
-
-  nsAutoString regValue; 
-  if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
-      !regValue.IsEmpty()) {
-    int red, green, blue;
-    ::swscanf(regValue.get(), L"%d,%d,%d", &red, &green, &blue);
-    ::sprintf(prefStringValue, "#%02X%02X%02X", red, green, blue);
-    aPrefs->SetCharPref(aPrefKeyName, prefStringValue);
-  }
-}
-
-// translate a windows registry DWORD int to a mozilla prefs PRInt32
-void
-TranslateDWORDtoPRInt32(nsIWindowsRegKey *aRegKey,
-                        const nsString& aRegValueName,
-                        nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  // clear previous settings with defaults
-  PRInt32 prefIntValue = 0;
-
-  if (NS_SUCCEEDED(aRegKey->ReadIntValue(aRegValueName, 
-                   reinterpret_cast<PRUint32 *>(&prefIntValue))))
-    aPrefs->SetIntPref(aPrefKeyName, prefIntValue);
-}
-
-// string copy
-void
-TranslateString(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-  nsAutoString regValue; 
-  if (NS_SUCCEEDED(aRegKey->ReadStringValue(aRegValueName, regValue)) &&
-      !regValue.IsEmpty()) {
-    aPrefs->SetCharPref(aPrefKeyName, NS_ConvertUTF16toUTF8(regValue).get());
-  }
-}
-
-// translate accepted language character set formats
-// (modified string copy)
-void
-TranslateLanglist(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                  nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  nsAutoString lang;
-  if (NS_FAILED(aRegKey->ReadStringValue(aRegValueName, lang)))
-      return;
-
-  // copy source format like "en-us,ar-kw;q=0.7,ar-om;q=0.3" into
-  // destination format like "en-us, ar-kw, ar-om"
-
-  char prefStringValue[MAX_PATH]; // a convenient size, one hopes
-  NS_LossyConvertUTF16toASCII langCstr(lang);
-  const char   *source = langCstr.get(),
-               *sourceEnd = source + langCstr.Length();
-  char   *dest = prefStringValue,
-         *destEnd = dest + (MAX_PATH-2); // room for " \0"
-  bool    skip = false,
-          comma = false;
-
-  while (source < sourceEnd && *source && dest < destEnd) {
-    if (*source == ',')
-      skip = false;
-    else if (*source == ';')
-      skip = true;
-    if (!skip) {
-      if (comma && *source != ' ')
-        *dest++ = ' ';
-      *dest++ = *source;
-    }
-    comma = *source == ',';
-    ++source;
-  }
-  *dest = 0;
-
-  aPrefs->SetCharPref(aPrefKeyName, prefStringValue);
-}
-
-static int CALLBACK
-fontEnumProc(const LOGFONTW *aLogFont, const TEXTMETRICW *aMetric,
-             DWORD aFontType, LPARAM aClosure) {
-  *((int *) aClosure) = aLogFont->lfPitchAndFamily & FF_ROMAN;
-  return 0;
-}
-void
-TranslatePropFont(nsIWindowsRegKey *aRegKey, const nsString& aRegValueName,
-                  nsIPrefBranch *aPrefs, char *aPrefKeyName) {
-
-  HDC      dc = ::GetDC(0);
-  LOGFONTW lf;
-  int      isSerif = 1;
-
-  // serif or sans-serif font?
-  lf.lfCharSet = DEFAULT_CHARSET;
-  lf.lfPitchAndFamily = 0;
-  nsAutoString font;
-  if (NS_FAILED(aRegKey->ReadStringValue(aRegValueName, font)))
-      return;
-
-  ::wcsncpy(lf.lfFaceName, font.get(), LF_FACESIZE);
-  lf.lfFaceName[LF_FACESIZE - 1] = L'\0';
-  ::EnumFontFamiliesExW(dc, &lf, fontEnumProc, (LPARAM) &isSerif, 0);
-  ::ReleaseDC(0, dc);
-
-  // XXX : For now, only x-western font is translated.
-  // All or Locale-dependent subset of fonts need to be translated.
-  nsDependentCString generic(isSerif ? "serif" : "sans-serif");
-  nsCAutoString prefName("font.name.");
-  prefName.Append(generic);
-  prefName.Append(".x-western");
-  aPrefs->SetCharPref(prefName.get(), NS_ConvertUTF16toUTF8(font).get());
-  aPrefs->SetCharPref("font.default.x-western", generic.get());
-}
-
-//***********************************************************************
-//*** master table of registry-to-gecko-pref translations
-//***********************************************************************
-
-struct regEntry {
-  char            *regKeyName,     // registry key (HKCU\Software ...)
-                  *regValueName;   // registry key leaf
-  char            *prefKeyName;    // pref name ("javascript.enabled" ...)
-  regEntryHandler  entryHandler;   // processing func
-};
-
-const regEntry gRegEntries[] = {
-  { "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete",
-     "AutoSuggest",
-     "browser.urlbar.autocomplete.enabled",
-     TranslateYNtoTF },
-  { "Software\\Microsoft\\Internet Explorer\\International",
-    "AcceptLanguage",
-    "intl.accept_languages",
-    TranslateLanglist },
-  // XXX : For now, only x-western font is translated.
-  // All or Locale-dependent subset of fonts need to be translated.
-  { "Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3",
-    "IEFixedFontName",
-    "font.name.monospace.x-western",
-    TranslateString },
-  { 0, // an optimization: 0 means use the previous key
-    "IEPropFontName",
-    "", // special-cased in the translation function
-    TranslatePropFont },
-  { "Software\\Microsoft\\Internet Explorer\\Main",
-    "Use_DlgBox_Colors",
-    "browser.display.use_system_colors",
-    TranslateYNtoTF },
-  { 0,
-    "Use FormSuggest",
-    "browser.formfill.enable",
-    TranslateYNtoTF },
-  { 0,
-    "FormSuggest Passwords",
-    "signon.rememberSignons",
-    TranslateYNtoTF },
-#if 0
-  // Firefox supplies its own home page.
-  { 0,
-    "Start Page",
-     REG_SZ,
-    "browser.startup.homepage",
-    TranslateString },
-#endif
-  { 0, 
-    "Anchor Underline",
-    "browser.underline_anchors",
-    TranslateYNtoTF },
-  { 0,
-    "Display Inline Images",
-    "permissions.default.image",
-    TranslateYNtoImageBehavior },
-  { 0,
-    "Enable AutoImageResize",
-    "browser.enable_automatic_image_resizing",
-    TranslateYNtoTF },
-  { 0,
-    "Move System Caret",
-    "accessibility.browsewithcaret",
-    TranslateYNtoTF },
-  { 0,
-    "NotifyDownloadComplete",
-    "browser.download.manager.showAlertOnComplete",
-    TranslateYNtoTF },
-  { 0,
-    "SmoothScroll",   // XXX DWORD 
-    "general.smoothScroll",
-    TranslateYNtoTF },
-  { "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
-    "EnableHttp1_1",
-    "network.http.version",
-    TranslateDWORDtoHTTPVersion },
-  { 0,
-    "ProxyHttp1.1",
-    "network.http.proxy.version",
-    TranslateDWORDtoHTTPVersion },
-// SecureProtocols
-  { "Software\\Microsoft\\Internet Explorer\\Settings",
-    "Always Use My Colors",            // XXX DWORD
-    "browser.display.use_document_colors",
-    TranslateYNtoFT },
-  { 0,
-    "Text Color",
-    "browser.display.foreground_color",
-    TranslateDRGBtoHRGB },
-  { 0,
-    "Background Color",
-    "browser.display.background_color",
-    TranslateDRGBtoHRGB },
-  { 0,
-    "Anchor Color",
-    "browser.anchor_color",
-    TranslateDRGBtoHRGB },
-  { 0,
-    "Anchor Color Visited",
-    "browser.visited_color",
-    TranslateDRGBtoHRGB },
-  { 0,
-    "Always Use My Font Face",    // XXX DWORD
-    "browser.display.use_document_fonts",
-    TranslateYNtoFT }
-};
-
-#if 0
-user_pref("font.size.fixed.x-western", 14);
-user_pref("font.size.variable.x-western", 15);
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// nsIBrowserProfileMigrator
-NS_IMETHODIMP
-nsIEProfileMigrator::Migrate(PRUint16 aItems, nsIProfileStartup* aStartup, const PRUnichar* aProfile)
-{
-  nsresult rv = NS_OK;
-
-  bool aReplace = false;
-
-  if (aStartup) {
-    aReplace = true;
-    rv = aStartup->DoStartup();
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull);
-
-  COPY_DATA(CopyPreferences,  aReplace, nsIBrowserProfileMigrator::SETTINGS);
-  COPY_DATA(CopyCookies,      aReplace, nsIBrowserProfileMigrator::COOKIES);
-  COPY_DATA(CopyHistory,      aReplace, nsIBrowserProfileMigrator::HISTORY);
-  COPY_DATA(CopyFormData,     aReplace, nsIBrowserProfileMigrator::FORMDATA);
-  COPY_DATA(CopyPasswords,    aReplace, nsIBrowserProfileMigrator::PASSWORDS);
-  COPY_DATA(CopyFavorites,    aReplace, nsIBrowserProfileMigrator::BOOKMARKS);
-
-  NOTIFY_OBSERVERS(MIGRATION_ENDED, nsnull);
-
-  return rv;
-}
-
-NS_IMETHODIMP
-nsIEProfileMigrator::GetMigrateData(const PRUnichar* aProfile, 
-                                    bool aReplace,
-                                    PRUint16* aResult)
-{
-  if (TestForIE7()) {
-    // IE7 and up store form data and passwords in an unrecoverable
-    // way, preventing us from importing this data.
-    *aResult = nsIBrowserProfileMigrator::SETTINGS |
-               nsIBrowserProfileMigrator::COOKIES | 
-               nsIBrowserProfileMigrator::HISTORY |
-               nsIBrowserProfileMigrator::BOOKMARKS;
-  }
-  else {
-    *aResult = nsIBrowserProfileMigrator::SETTINGS |
-               nsIBrowserProfileMigrator::COOKIES | 
-               nsIBrowserProfileMigrator::HISTORY |
-               nsIBrowserProfileMigrator::FORMDATA |
-               nsIBrowserProfileMigrator::PASSWORDS |
-               nsIBrowserProfileMigrator::BOOKMARKS;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsIEProfileMigrator::GetSourceExists(bool* aResult)
-{
-  // IE always exists. 
-  *aResult = true;
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsIEProfileMigrator::GetSourceProfiles(JS::Value* aResult)
-{
-  *aResult = JSVAL_NULL;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsIEProfileMigrator::GetSourceHomePageURL(nsACString& aResult)
-{
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (!regKey ||
-      NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                             REGISTRY_IE_MAIN_KEY,
-                             nsIWindowsRegKey::ACCESS_READ)))
-    return NS_OK;
-  // Read in the main home page
-  NS_NAMED_LITERAL_STRING(homeURLValName, "Start Page");
-  nsAutoString homeURLVal;
-
-  if (NS_SUCCEEDED(regKey->ReadStringValue(homeURLValName, homeURLVal))) {
-    // Do we need this round-about way to get |homePageURL|? 
-    // Perhaps, we do to have the form of URL under our control 
-    // (cf. network.standard-url.escape-utf8)
-    // Note that Windows stores URLs in IRI in the registry
-    nsCAutoString  homePageURL;
-    nsCOMPtr<nsIURI> homePageURI;
-
-    if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(homePageURI), homeURLVal))) {
-      if (NS_SUCCEEDED(homePageURI->GetSpec(homePageURL)) && !homePageURL.IsEmpty()) {
-          aResult.Assign(homePageURL);
-      }
-    }
-  }
-
-  // With IE7, The "Start Page" key still exists. Secondary home pages
-  // are located in a string stored in "Secondary Start Pages" which
-  // contains multiple Unicode URI seperated by nulls. (REG_MULTI_SZ)
-  NS_NAMED_LITERAL_STRING(ssRegKeyName, "Secondary Start Pages");
-  nsAutoString secondaryList;
-
-  if (NS_SUCCEEDED(regKey->ReadStringValue(ssRegKeyName, secondaryList)) &&
-      !secondaryList.IsEmpty()) {
-    nsTArray<nsCString> parsedList;
-    if (!ParseString(NS_ConvertUTF16toUTF8(secondaryList), '\0', parsedList))
-      return NS_OK;
-
-    // Split the result up into individual uri
-    for (PRUint32 index = 0; index < parsedList.Length(); ++index) {
-      nsCOMPtr<nsIURI> uri;
-      nsCAutoString homePage;
-      // Append "|uri" to result. This is how we currently handle
-      // storing multiple home pages.
-      if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), parsedList[index]))) {
-        if (NS_SUCCEEDED(uri->GetSpec(homePage)) && !homePage.IsEmpty()) {
-            aResult.AppendLiteral("|");
-            aResult.Append(homePage);
-        }
-      }
-    }
-  }
-
-  return NS_OK;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// nsIEProfileMigrator
-NS_IMPL_ISUPPORTS2(nsIEProfileMigrator, nsIBrowserProfileMigrator, nsINavHistoryBatchCallback);
-
-nsIEProfileMigrator::nsIEProfileMigrator()
-{
-  mObserverService = do_GetService("@mozilla.org/observer-service;1");
-}
-
-nsIEProfileMigrator::~nsIEProfileMigrator() 
-{
-}
-
-// Test used in detecting Internet Explorer 7 prior to presenting
-// import options.
-bool 
-nsIEProfileMigrator::TestForIE7()
-{
-  nsCOMPtr<nsIWindowsRegKey> regKey =  
-    do_CreateInstance("@mozilla.org/windows-registry-key;1"); 
-  if (!regKey)  
-    return false;  
-
-  NS_NAMED_LITERAL_STRING(key,
-      "Applications\\iexplore.exe\\shell\\open\\command");
-  if (NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
-                             key, nsIWindowsRegKey::ACCESS_QUERY_VALUE)))
-    return false;
-
-  nsAutoString iePath;
-  if (NS_FAILED(regKey->ReadStringValue(EmptyString(), iePath)))
-    return false; 
-
-  // Replace embedded environment variables. 
-  PRUint32 bufLength =  
-    ::ExpandEnvironmentStringsW(iePath.get(), 
-                               L"", 0); 
-  if (bufLength == 0) // Error 
-    return false; 
-
-  nsAutoArrayPtr<PRUnichar> destination(new PRUnichar[bufLength]); 
-  if (!destination) 
-    return false; 
-
-  if (!::ExpandEnvironmentStringsW(iePath.get(), 
-                                   destination, 
-                                   bufLength)) 
-    return false; 
-
-  iePath = destination; 
-
-  // Check if the path is enclosed in quotation marks.
-  if (StringBeginsWith(iePath, NS_LITERAL_STRING("\""))) {
-    iePath.Cut(0,1);
-    PRInt32 index = iePath.FindChar('\"', 0);
-
-    // After removing the opening quoation mark,
-    // remove the closing one and everything after it.
-    if (index > 0)
-      iePath.Cut(index,iePath.Length());
-  }
-
-  nsCOMPtr<nsILocalFile> lf; 
-  NS_NewLocalFile(iePath, true, getter_AddRefs(lf)); 
-
-  nsCOMPtr<nsILocalFileWin> lfw = do_QueryInterface(lf); 
-  if (!lfw)
-   return false;
-   
-  nsAutoString ieVersion;
-  if (NS_FAILED(lfw->GetVersionInfoField("FileVersion", ieVersion)))
-   return false;
-
-  if (ieVersion.Length() > 2) {
-    PRInt32 index = ieVersion.FindChar('.', 0);
-    if (index == kNotFound)
-      return false;
-    ieVersion.Cut(index, ieVersion.Length());
-    PRInt32 ver = wcstol(ieVersion.get(), nsnull, 0);
-    if (ver >= 7) // Found 7 or greater major version
-      return true;
-  }
-
-  return false;
-}
-
-NS_IMETHODIMP
-nsIEProfileMigrator::RunBatched(nsISupports* aUserData)
-{
-  PRUint8 batchAction;
-  nsCOMPtr<nsISupportsPRUint8> strWrapper(do_QueryInterface(aUserData));
-  NS_ASSERTION(strWrapper, "Unable to create nsISupportsPRUint8 wrapper!");
-  nsresult rv = strWrapper->GetData(&batchAction);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  switch (batchAction) {
-    case BATCH_ACTION_HISTORY:
-      rv = CopyHistoryBatched(false);
-      break;
-    case BATCH_ACTION_HISTORY_REPLACE:
-      rv = CopyHistoryBatched(true);
-      break;
-    case BATCH_ACTION_BOOKMARKS:
-      rv = CopyFavoritesBatched(false);
-      break;
-    case BATCH_ACTION_BOOKMARKS_REPLACE:
-      rv = CopyFavoritesBatched(true);
-      break;
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-nsIEProfileMigrator::CopyHistory(bool aReplace)
-{
-  nsresult rv;
-  nsCOMPtr<nsINavHistoryService> history =
-    do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint8 batchAction = aReplace ? BATCH_ACTION_HISTORY_REPLACE
-                                 : BATCH_ACTION_HISTORY;
-  nsCOMPtr<nsISupportsPRUint8> supports =
-    do_CreateInstance(NS_SUPPORTS_PRUINT8_CONTRACTID);
-  NS_ENSURE_TRUE(supports, NS_ERROR_OUT_OF_MEMORY);
-  rv = supports->SetData(batchAction);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = history->RunInBatchMode(this, supports);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-nsIEProfileMigrator::CopyHistoryBatched(bool aReplace)
-{
-  nsCOMPtr<nsIBrowserHistory> hist(do_GetService(NS_GLOBALHISTORY2_CONTRACTID));
-  nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
-
-  // First, Migrate standard IE History entries...
-  ::CoInitialize(NULL);
-
-  IUrlHistoryStg2* ieHistory;
-  HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory, 
-                                  NULL,
-                                  CLSCTX_INPROC_SERVER, 
-                                  IID_IUrlHistoryStg2, 
-                                  reinterpret_cast<void**>(&ieHistory));
-  if (SUCCEEDED(hr)) {
-    IEnumSTATURL* enumURLs;
-    hr = ieHistory->EnumUrls(&enumURLs);
-    if (SUCCEEDED(hr)) {
-      STATURL statURL;
-      ULONG fetched;
-      _bstr_t url;
-      nsCAutoString scheme;
-      SYSTEMTIME st;
-      bool validScheme = false;
-      PRUnichar* tempTitle = nsnull;
-
-      for (int count = 0; (hr = enumURLs->Next(1, &statURL, &fetched)) == S_OK; ++count) {
-        if (statURL.pwcsUrl) {
-          // 1 - Page Title
-          tempTitle = statURL.pwcsTitle ? (PRUnichar*)((wchar_t*)(statURL.pwcsTitle)) : nsnull;
-
-          // 2 - Last Visit Date
-          ::FileTimeToSystemTime(&(statURL.ftLastVisited), &st);
-          PRExplodedTime prt;
-          prt.tm_year = st.wYear;
-          prt.tm_month = st.wMonth - 1; // SYSTEMTIME's day-of-month parameter is 1-based, PRExplodedTime's is 0-based.
-          prt.tm_mday = st.wDay;
-          prt.tm_hour = st.wHour;
-          prt.tm_min = st.wMinute;
-          prt.tm_sec = st.wSecond;
-          prt.tm_usec = st.wMilliseconds * 1000;
-          prt.tm_wday = 0;
-          prt.tm_yday = 0;
-          prt.tm_params.tp_gmt_offset = 0;
-          prt.tm_params.tp_dst_offset = 0;
-          PRTime lastVisited = PR_ImplodeTime(&prt);
-
-          // 3 - URL
-          url = statURL.pwcsUrl;
-
-          NS_ConvertUTF16toUTF8 urlStr(url);
-
-          if (NS_FAILED(ios->ExtractScheme(urlStr, scheme))) {
-            ::CoTaskMemFree(statURL.pwcsUrl);    
-            if (statURL.pwcsTitle) 
-              ::CoTaskMemFree(statURL.pwcsTitle);
-            continue;
-          }
-          ToLowerCase(scheme);
-
-          // XXXben - 
-          // MSIE stores some types of URLs in its history that we can't handle, like HTMLHelp
-          // and others. At present Necko isn't clever enough to delegate handling of these types
-          // to the system, so we should just avoid importing them. 
-          const char* schemes[] = { "http", "https", "ftp", "file" };
-          for (int i = 0; i < 4; ++i) {
-            if (validScheme = scheme.Equals(schemes[i]))
-              break;
-          }
-          
-          // 4 - Now add the page
-          if (validScheme) {
-            nsCOMPtr<nsIURI> uri;
-            ios->NewURI(urlStr, nsnull, nsnull, getter_AddRefs(uri));
-            if (uri) {
-              if (tempTitle) 
-                hist->AddPageWithDetails(uri, tempTitle, lastVisited);
-              else
-                hist->AddPageWithDetails(uri, url, lastVisited);
-            }
-          }
-          ::CoTaskMemFree(statURL.pwcsUrl);    
-        }
-        if (statURL.pwcsTitle) 
-          ::CoTaskMemFree(statURL.pwcsTitle);    
-      }
-      nsCOMPtr<nsIRDFRemoteDataSource> ds(do_QueryInterface(hist));
-      if (ds)
-        ds->Flush();
-  
-      enumURLs->Release();
-    }
-
-    ieHistory->Release();
-  }
-  ::CoUninitialize();
-
-  // Now, find out what URLs were typed in by the user
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (regKey && 
-      NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                REGISTRY_IE_TYPEDURL_KEY,
-                                nsIWindowsRegKey::ACCESS_READ))) {
-    int offset = 0;
-
-    while (1) {
-      nsAutoString valueName;
-      if (NS_FAILED(regKey->GetValueName(offset, valueName)))
-        break;
-
-      nsAutoString url; 
-      if (Substring(valueName, 0, 3).EqualsLiteral("url") &&
-          NS_SUCCEEDED(regKey->ReadStringValue(valueName, url))) {
-        nsCOMPtr<nsIURI> uri;
-        ios->NewURI(NS_ConvertUTF16toUTF8(url), nsnull, nsnull,
-                    getter_AddRefs(uri));
-        if (uri)
-          hist->MarkPageAsTyped(uri);
-      }
-      ++offset;
-    }
-  }
-
-  return NS_OK;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// IE PASSWORDS AND FORM DATA - A PROTECTED STORAGE SYSTEM PRIMER
-//
-// Internet Explorer 4.0 and up store sensitive form data (saved through form
-// autocomplete) and saved passwords in a special area of the Registry called
-// the Protected Storage System. The data IE stores in the Protected Storage 
-// System is located under:
-//
-// HKEY_CURRENT_USER\Software\Microsoft\Protected Storage System Provider\
-//   <USER_ID>\Data\<IE_PSS_GUID>\<IE_PSS_GUID>\
-//
-// <USER_ID> is a long string that uniquely identifies the current user
-// <IE_PSS_GUID> is a GUID that identifies a subsection of the Protected Storage
-// System specific to MSIE. This GUID is defined below ("IEPStoreAutocompGUID").
-//
-// Data is stored in the Protected Strage System ("PStore") in the following
-// format:
-// 
-// <IE_PStore_Key> \
-//                  fieldName1:StringData \ ItemData = <REG_BINARY>
-//                  fieldName2:StringData \ ItemData = <REG_BINARY>
-//                  http://foo.com/login.php:StringData \ ItemData = <REG_BINARY>
-//                  ... etc ...
-// 
-// Each key represents either the identifier of a web page text field that 
-// data was saved from (e.g. <input name="fieldName1">), or a URL that a login
-// (username + password) was saved at (e.g. "http://foo.com/login.php")
-//
-// Data is stored for each of these cases in the following format:
-//
-// for both types of data, the Value ItemData is REG_BINARY data format encrypted with 
-// a 3DES cipher. 
-//
-// for FormData: the decrypted data is in the form:
-//                  value1\0value2\0value3\0value4 ...
-// for Signons:  the decrypted data is in the form:
-//                  username\0password
-//
-// We cannot read the PStore directly by using Registry functions because the 
-// keys have limited read access such that only System process can read from them.
-// In order to read from the PStore we need to use Microsoft's undocumented PStore
-// API(*). 
-//
-// (* Sparse documentation became available as of the January 2004 MSDN Library
-//    release)
-//
-// The PStore API lets us read decrypted data from the PStore. Encryption does not
-// appear to be strong.
-// 
-// Details on how each type of data is read from the PStore and migrated appropriately
-// is discussed in more detail below.
-//
-// For more information about the PStore, read:
-//
-// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devnotes/winprog/pstore.asp
-//
-
-
-typedef HRESULT (WINAPI *PStoreCreateInstancePtr)(IPStore**, DWORD, DWORD, DWORD);
-
-// IE PStore Type GUIDs
-// {e161255a-37c3-11d2-bcaa-00c04fd929db} Autocomplete Password & Form Data
-//                                        Subtype has same GUID
-// {5e7e8100-9138-11d1-945a-00c04fc308ff} HTTP/FTP Auth Login Data
-//                                        Subtype has a zero GUID
-static GUID IEPStoreAutocompGUID = { 0xe161255a, 0x37c3, 0x11d2, { 0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb } };
-static GUID IEPStoreSiteAuthGUID = { 0x5e7e8100, 0x9138, 0x11d1, { 0x94, 0x5a, 0x00, 0xc0, 0x4f, 0xc3, 0x08, 0xff } };
-
-///////////////////////////////////////////////////////////////////////////////
-// IMPORTING AUTOCOMPLETE PASSWORDS
-//
-// This is tricky, and requires 2 passes through the subkeys in IE's PStore 
-// section.
-//
-// First, we walk IE's PStore section, looking for subkeys that are prefixed
-// with a URI. As mentioned above, we assume all such subkeys are stored 
-// passwords.
-//
-//   http://foo.com/login.php:StringData   username\0password
-//
-// We can't add this item to the Password Manager just yet though. 
-//
-// The password manager requires the uniquifier of the username field (that is,
-// the value of the "name" or "id" attribute) from the webpage so that it can
-// prefill the value automatically. IE doesn't store this information with
-// the password entry, but it DOES store this information independently as a 
-// separate Form Data entry. 
-//
-// In other words, if you go to foo.com above and log in with "username" and
-// "password" as your details in a form where the username field is uniquified
-// as "un" (<input type="text" name="un">), when you login and elect to have IE 
-// save the password for the site, the following TWO entries are created in IE's
-// PStore section:
-//
-//   http://foo.com/login.php:StringData   username\0password
-//   un:StringData                         username
-//
-// Thus to discover the field name for each login we need to first gather up 
-// all the signons (collecting usernames in the process), then walk the list
-// again, looking ONLY at non-URI prefixed subkeys, and searching for each
-// username as a value in each such subkey's value list. If we have a match, 
-// we assume that the subkey (with its uniquifier prefix) is a login field. 
-//
-// With this information, we call Password Manager's "AddLogin" method 
-// providing this detail. We don't need to provide the password field name, 
-// we have no means of retrieving this info from IE, and the Password Manager
-// knows to hunt for a password field near the login field if none is specified.
-//
-// IMPLICATIONS:
-//  1) redundant signon entries for non-login forms might be created, but these
-//     should be benign.
-//  2) if the IE user ever clears his Form AutoComplete cache but doesn't clear
-//     his passwords, we will be hosed, as we have no means of locating the
-//     username field. Maybe someday the Password Manager will become 
-//     artificially intelligent and be able to guess where the login fields are,
-//     but I'm not holding my breath. 
-//
-
-nsresult
-nsIEProfileMigrator::CopyPasswords(bool aReplace)
-{
-  HRESULT hr;
-  nsresult rv;
-  nsTArray<SignonData> signonsFound;
-
-  HMODULE pstoreDLL = ::LoadLibraryW(L"pstorec.dll");
-  if (!pstoreDLL) {
-    // XXXben TODO
-    // Need to figure out what to do here on Windows 98 etc... it may be that the key is universal read
-    // and we can just blunder into the registry and use CryptUnprotect to get the data out. 
-    return NS_ERROR_FAILURE;
-  }
-
-  PStoreCreateInstancePtr PStoreCreateInstance = (PStoreCreateInstancePtr)::GetProcAddress(pstoreDLL, "PStoreCreateInstance");
-  IPStore* PStore;
-  hr = PStoreCreateInstance(&PStore, 0, 0, 0);
-
-  rv = GetSignonsListFromPStore(PStore, &signonsFound);
-  if (NS_SUCCEEDED(rv))
-    ResolveAndMigrateSignons(PStore, &signonsFound);
-
-  MigrateSiteAuthSignons(PStore);
-  return NS_OK;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// IMPORTING SITE AUTHENTICATION PASSWORDS
-//
-// This is simple and straightforward. We iterate through the part of the 
-// PStore that matches the type GUID defined in IEPStoreSiteAuthGUID and
-// a zero subtype GUID. For each item, we check the data for a ':' that
-// separates the username and password parts. If there is no ':', we give up.
-// After that, we check to see if the name of the item starts with "DPAPI:".
-// We bail out if that's the case, because we can't handle those yet.
-// However, if everything is all and well, we convert the itemName to a realm
-// string that the password manager can work with and save this login
-// via AddLogin.
-
-nsresult
-nsIEProfileMigrator::MigrateSiteAuthSignons(IPStore* aPStore)
-{
-  HRESULT hr;
-
-  NS_ENSURE_ARG_POINTER(aPStore);
-
-  nsCOMPtr<nsILoginManagerIEMigrationHelper> pwmgr(
-    do_GetService("@mozilla.org/login-manager/storage/legacy;1"));
-  if (!pwmgr)
-    return NS_OK;
-
-  GUID mtGuid = {0};
-  IEnumPStoreItems* enumItems = NULL;
-  hr = aPStore->EnumItems(0, &IEPStoreSiteAuthGUID, &mtGuid, 0, &enumItems);
-  if (SUCCEEDED(hr) && enumItems != NULL) {
-    LPWSTR itemName = NULL;
-    while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
-      unsigned long count = 0;
-      unsigned char* data = NULL;
-
-      hr = aPStore->ReadItem(0, &IEPStoreSiteAuthGUID, &mtGuid, itemName,
-                             &count, &data, NULL, 0);
-      if (SUCCEEDED(hr) && data) {
-        unsigned long i;
-        unsigned char* password = NULL;
-        for (i = 0; i < count; i++)
-          if (data[i] == ':') {
-            data[i] = '\0';
-            if (i + 1 < count)
-              password = &data[i + 1];
-            break;
-          }
-
-        nsAutoString host(itemName), realm;
-        if (Substring(host, 0, 6).EqualsLiteral("DPAPI:")) // often FTP logins
-          password = NULL; // We can't handle these yet
-
-        if (password) {
-          int idx;
-          idx = host.FindChar('/');
-          if (idx) {
-            realm.Assign(Substring(host, idx + 1));
-            host.Assign(Substring(host, 0, idx));
-          }
-          // XXX: username and password are always ASCII in IPStore?
-          // If not, are they in UTF-8 or the default codepage? (ref. bug 41489)
-          nsresult rv;
-
-          nsCOMPtr<nsILoginInfo> aLogin (do_CreateInstance(
-                                           NS_LOGININFO_CONTRACTID, &rv));
-          NS_ENSURE_SUCCESS(rv, rv);
-
-          // nsStringAPI doesn't let us create void strings, so we won't
-          // use Init() here.
-          aLogin->SetHostname(host);
-          aLogin->SetHttpRealm(realm);
-          aLogin->SetUsername(NS_ConvertUTF8toUTF16((char *)data));
-          aLogin->SetPassword(NS_ConvertUTF8toUTF16((char *)password));
-          aLogin->SetUsernameField(EmptyString());
-          aLogin->SetPasswordField(EmptyString());
-
-          pwmgr->MigrateAndAddLogin(aLogin);
-        }
-        ::CoTaskMemFree(data);
-      }
-    }
-  }
-  return NS_OK;
-}
-
-nsresult
-nsIEProfileMigrator::GetSignonsListFromPStore(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound)
-{
-  HRESULT hr;
-
-  NS_ENSURE_ARG_POINTER(aPStore);
-
-  IEnumPStoreItems* enumItems = NULL;
-  hr = aPStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
-  if (SUCCEEDED(hr) && enumItems != NULL) {
-    LPWSTR itemName = NULL;
-    while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
-      unsigned long count = 0;
-      unsigned char* data = NULL;
-
-      // We are responsible for freeing |data| using |CoTaskMemFree|!!
-      // But we don't do it here... 
-      hr = aPStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
-      if (SUCCEEDED(hr) && data) {
-        nsAutoString itemNameString(itemName);
-        if (StringTail(itemNameString, 11).
-            LowerCaseEqualsLiteral(":stringdata")) {
-          // :StringData contains the saved data
-          const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
-          char* realm = nsnull;
-          if (KeyIsURI(key, &realm)) {
-            // This looks like a URL and could be a password. If it has username and password data, then we'll treat
-            // it as one and add it to the password manager
-            unsigned char* username = NULL;
-            unsigned char* pass = NULL;
-            GetUserNameAndPass(data, count, &username, &pass);
-
-            if (username && pass) {
-              // username and pass are pointers into the data buffer allocated by IPStore's ReadItem
-              // method, and we own that buffer. We don't free it here, since we're going to be using 
-              // it after the password harvesting stage to locate the username field. Only after the second
-              // phase is complete do we free the buffer. 
-              SignonData* d = aSignonsFound->AppendElement();
-              if (!d)
-                return NS_ERROR_OUT_OF_MEMORY;
-              d->user = (PRUnichar*)username;
-              d->pass = (PRUnichar*)pass;
-              d->realm = realm; // freed in ResolveAndMigrateSignons
-            }
-          }
-        }
-      }
-    }
-  }
-  return NS_OK;
-}
-
-bool
-nsIEProfileMigrator::KeyIsURI(const nsAString& aKey, char** aRealm)
-{
-  *aRealm = nsnull;
-
-  nsCOMPtr<nsIURI> uri;
-
-  if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), aKey))) 
-    return false;
-
-  bool validScheme = false;
-  const char* schemes[] = { "http", "https" };
-  for (int i = 0; i < 2; ++i) {
-    uri->SchemeIs(schemes[i], &validScheme);
-    if (validScheme) {
-      nsCAutoString realm;
-      uri->GetScheme(realm);
-      realm.AppendLiteral("://");
-
-      nsCAutoString host;
-      uri->GetHost(host);
-      realm.Append(host);
-
-      *aRealm = ToNewCString(realm);
-      return validScheme;
-    }
-  }
-  return false;
-}
-
-nsresult
-nsIEProfileMigrator::ResolveAndMigrateSignons(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound)
-{
-  HRESULT hr;
-
-  IEnumPStoreItems* enumItems = NULL;
-  hr = aPStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
-  if (SUCCEEDED(hr) && enumItems != NULL) {
-    LPWSTR itemName = NULL;
-    while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
-      unsigned long count = 0;
-      unsigned char* data = NULL;
-
-      hr = aPStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
-      if (SUCCEEDED(hr) && data) {
-        nsAutoString itemNameString(itemName);
-        if (StringTail(itemNameString, 11).
-            LowerCaseEqualsLiteral(":stringdata")) {
-          // :StringData contains the saved data
-          const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
-          
-          // Assume all keys that are valid URIs are signons, not saved form data, and that 
-          // all keys that aren't valid URIs are form field names (containing form data).
-          nsCString realm;
-          if (!KeyIsURI(key, getter_Copies(realm))) {
-            // Search the data for a username that matches one of the found signons. 
-            EnumerateUsernames(key, (PRUnichar*)data, (count/sizeof(PRUnichar)), aSignonsFound);
-          }
-        }
-
-        ::CoTaskMemFree(data);
-      }
-    }
-    // Now that we've done resolving signons, we need to walk the signons list, freeing the data buffers 
-    // for each SignonData entry, since these buffers were allocated by the system back in |GetSignonListFromPStore|
-    // but never freed. 
-    PRUint32 signonCount = aSignonsFound->Length();
-    for (PRUint32 i = 0; i < signonCount; ++i) {
-      SignonData &sd = aSignonsFound->ElementAt(i);
-      ::CoTaskMemFree(sd.user);  // |sd->user| is a pointer to the start of a buffer that also contains sd->pass
-      NS_Free(sd.realm);
-    }
-    aSignonsFound->Clear();
-  }
-  return NS_OK;
-}
-
-void
-nsIEProfileMigrator::EnumerateUsernames(const nsAString& aKey, PRUnichar* aData, unsigned long aCount, nsTArray<SignonData>* aSignonsFound)
-{
-  nsCOMPtr<nsILoginManagerIEMigrationHelper> pwmgr(
-    do_GetService("@mozilla.org/login-manager/storage/legacy;1"));
-  if (!pwmgr)
-    return;
-
-  PRUnichar* cursor = aData;
-  PRUint32 offset = 0;
-  PRUint32 signonCount = aSignonsFound->Length();
-
-  while (offset < aCount) {
-    nsAutoString curr; curr = cursor;
-
-    // Compare the value at the current cursor position with the collected list of signons
-    for (PRUint32 i = 0; i < signonCount; ++i) {
-      SignonData &sd = aSignonsFound->ElementAt(i);
-      if (curr.Equals(sd.user)) {
-        // Bingo! Found a username in the saved data for this item. Now, add a Signon.
-        nsDependentString usernameStr(sd.user), passStr(sd.pass);
-        nsAutoString realm(NS_ConvertUTF8toUTF16(sd.realm));
-
-        nsresult rv;
-
-        nsCOMPtr<nsILoginInfo> aLogin (do_CreateInstance(NS_LOGININFO_CONTRACTID, &rv));
-        NS_ENSURE_SUCCESS(rv, /* */);
-
-        // nsStringAPI doesn't let us create void strings, so we won't
-        // use Init() here.
-        // IE doesn't have the form submit URL, so set to empty-string,
-        // which the login manager uses as a wildcard value.
-        // IE doesn't store the password field name either, so just set it
-        // to an empty string.
-        aLogin->SetHostname(realm);
-        aLogin->SetFormSubmitURL(EmptyString());
-        aLogin->SetUsername(usernameStr);
-        aLogin->SetPassword(passStr);
-        aLogin->SetUsernameField(aKey);
-        aLogin->SetPasswordField(EmptyString());
-
-        pwmgr->MigrateAndAddLogin(aLogin);
-      }
-    }
-
-    // Advance the cursor
-    PRInt32 advance = curr.Length() + 1;
-    cursor += advance; // Advance to next string (length of curr string + 1 PRUnichar for null separator)
-    offset += advance;
-  } 
-}
-
-void 
-nsIEProfileMigrator::GetUserNameAndPass(unsigned char* data, unsigned long len, unsigned char** username, unsigned char** pass)
-{
-  *username = data;
-  *pass = NULL;
-
-  unsigned char* temp = data; 
-
-  for (unsigned int i = 0; i < len; i += 2, temp += 2*sizeof(unsigned char)) {
-    if (*temp == '\0') {
-      *pass = temp + 2*sizeof(unsigned char);
-      break;
-    }
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// IMPORTING FORM DATA
-//
-// This is a much simpler task as all we need is the field name and that's part
-// of the key used to identify each data set in the PStore. The algorithm here
-// is as follows:
-//
-//   fieldName1:StringData    value1\0value2\0value3\0
-//   fieldName2:StringData    value1\0value2\0value3\0
-//   fieldName3:StringData    value1\0value2\0value3\0
-//
-// Walk each non-URI prefixed key in IE's PStore section, split the value provided
-// into chunks (\0 delimited) and use nsIFormHistory2's |addEntry| method to add
-// an entry for the fieldName prefix and each value. 
-//
-// "Quite Easily Done". ;-)
-// 
-nsresult
-nsIEProfileMigrator::CopyFormData(bool aReplace)
-{
-  HRESULT hr;
-
-  HMODULE pstoreDLL = ::LoadLibraryW(L"pstorec.dll");
-  if (!pstoreDLL) {
-    // XXXben TODO
-    // Need to figure out what to do here on Windows 98 etc... it may be that the key is universal read
-    // and we can just blunder into the registry and use CryptUnprotect to get the data out. 
-    return NS_ERROR_FAILURE;
-  }
-
-  PStoreCreateInstancePtr PStoreCreateInstance = (PStoreCreateInstancePtr)::GetProcAddress(pstoreDLL, "PStoreCreateInstance");
-  IPStore* PStore = NULL;
-  hr = PStoreCreateInstance(&PStore, 0, 0, 0);
-  if (FAILED(hr) || PStore == NULL)
-    return NS_OK;
-
-  IEnumPStoreItems* enumItems = NULL;
-  hr = PStore->EnumItems(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, 0, &enumItems);
-  if (SUCCEEDED(hr) && enumItems != NULL) {
-    LPWSTR itemName = NULL;
-    while ((enumItems->Next(1, &itemName, 0) == S_OK) && itemName) {
-      unsigned long count = 0;
-      unsigned char* data = NULL;
-
-      // We are responsible for freeing |data| using |CoTaskMemFree|!!
-      hr = PStore->ReadItem(0, &IEPStoreAutocompGUID, &IEPStoreAutocompGUID, itemName, &count, &data, NULL, 0);
-      if (SUCCEEDED(hr) && data) {
-        nsAutoString itemNameString(itemName);
-        if (StringTail(itemNameString, 11).
-            LowerCaseEqualsLiteral(":stringdata")) {
-          // :StringData contains the saved data
-          const nsAString& key = Substring(itemNameString, 0, itemNameString.Length() - 11);
-          nsCString realm;
-          if (!KeyIsURI(key, getter_Copies(realm))) {
-            nsresult rv = AddDataToFormHistory(key, (PRUnichar*)data, (count/sizeof(PRUnichar)));
-            if (NS_FAILED(rv)) return rv;
-          }
-        }
-      }
-    }
-  }
-  return NS_OK;
-}
-
-nsresult
-nsIEProfileMigrator::AddDataToFormHistory(const nsAString& aKey, PRUnichar* aData, unsigned long aCount)
-{
-  nsCOMPtr<nsIFormHistory2> formHistory(do_GetService("@mozilla.org/satchel/form-history;1"));
-  if (!formHistory)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  PRUnichar* cursor = aData;
-  PRUint32 offset = 0;
-
-  while (offset < aCount) {
-    nsAutoString curr; curr = cursor;
-
-    formHistory->AddEntry(aKey, curr);
-
-    // Advance the cursor
-    PRInt32 advance = curr.Length() + 1;
-    cursor += advance; // Advance to next string (length of curr string + 1 PRUnichar for null separator)
-    offset += advance;
-  } 
-
-  return NS_OK;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// favorites
-// search keywords
-//
-nsresult
-nsIEProfileMigrator::CopyFavorites(bool aReplace)
-{
-  nsresult rv;
-  nsCOMPtr<nsINavBookmarksService> bookmarks =
-    do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint8 batchAction = aReplace ? BATCH_ACTION_BOOKMARKS_REPLACE
-                                 : BATCH_ACTION_BOOKMARKS;
-  nsCOMPtr<nsISupportsPRUint8> supports =
-    do_CreateInstance(NS_SUPPORTS_PRUINT8_CONTRACTID);
-  NS_ENSURE_TRUE(supports, NS_ERROR_OUT_OF_MEMORY);
-  rv = supports->SetData(batchAction);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = bookmarks->RunInBatchMode(this, supports);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult
-nsIEProfileMigrator::CopyFavoritesBatched(bool aReplace)
-{
-  // If "aReplace" is true, merge into the root level of bookmarks. Otherwise,
-  // create a folder called "Imported IE Favorites" and place all the Bookmarks
-  // there.
-  nsresult rv;
-
-  nsCOMPtr<nsINavBookmarksService> bms =
-    do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRInt64 bookmarksMenuFolderId;
-  rv = bms->GetBookmarksMenuFolder(&bookmarksMenuFolderId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoString personalToolbarFolderName;
-  PRInt64 folder;
-  if (!aReplace) {
-    nsCOMPtr<nsIStringBundleService> bundleService =
-      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIStringBundle> bundle;
-    rv = bundleService->CreateBundle(TRIDENTPROFILE_BUNDLE,
-                                     getter_AddRefs(bundle));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsString sourceNameIE;
-    rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameIE").get(),
-                                   getter_Copies(sourceNameIE));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    const PRUnichar* sourceNameStrings[] = { sourceNameIE.get() };
-    nsString importedIEFavsTitle;
-    rv = bundle->FormatStringFromName(NS_LITERAL_STRING("importedBookmarksFolder").get(),
-                                      sourceNameStrings, 1,
-                                      getter_Copies(importedIEFavsTitle));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = bms->CreateFolder(bookmarksMenuFolderId,
-                           NS_ConvertUTF16toUTF8(importedIEFavsTitle),
-                           nsINavBookmarksService::DEFAULT_INDEX,
-                           &folder);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
-    // Locate the Links toolbar folder, we want to replace the Personal Toolbar
-    // content with Favorites in this folder.
-    // On versions minor or equal to IE6 the folder name is stored in the
-    // LinksFolderName registry key, but in newer versions it may be just a
-    // Links subfolder inside the default Favorites folder.
-    nsCOMPtr<nsIWindowsRegKey> regKey =
-      do_CreateInstance("@mozilla.org/windows-registry-key;1");
-    if (regKey &&
-        NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                  REGISTRY_IE_TOOLBAR_KEY,
-                                  nsIWindowsRegKey::ACCESS_READ))) {
-      nsAutoString linksFolderName;
-      if (NS_SUCCEEDED(regKey->ReadStringValue(
-                         NS_LITERAL_STRING("LinksFolderName"),
-                         linksFolderName))) {
-        personalToolbarFolderName = linksFolderName;
-      }
-      else {
-        personalToolbarFolderName.AssignLiteral("Links");
-      }
-    }
-
-    folder = bookmarksMenuFolderId;
-  }
-
-  nsCOMPtr<nsIProperties> fileLocator =
-    do_GetService("@mozilla.org/file/directory_service;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIFile> favoritesDirectory;
-  (void)fileLocator->Get(NS_WIN_FAVORITES_DIR, NS_GET_IID(nsIFile),
-                         getter_AddRefs(favoritesDirectory));
-
-  // If |favoritesDirectory| is null, it means that we're on a Windows
-  // platform that does not have a Favorites folder, e.g. Windows 95
-  // (early SRs, before IE integrated with the shell).
-  // Only try to read Favorites folder if it exists on the machine.
-  if (favoritesDirectory) {
-    rv = ParseFavoritesFolder(favoritesDirectory, folder, bms,
-                              personalToolbarFolderName, true);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return CopySmartKeywords(bms, bookmarksMenuFolderId);
-}
-
-nsresult
-nsIEProfileMigrator::CopySmartKeywords(nsINavBookmarksService* aBMS,
-                                       PRInt64 aParentFolder)
-{ 
-  nsresult rv;
-
-  nsCOMPtr<nsIWindowsRegKey> regKey =
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (regKey && 
-      NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                REGISTRY_IE_SEARCHURL_KEY,
-                                nsIWindowsRegKey::ACCESS_READ))) {
-
-    nsCOMPtr<nsIStringBundleService> bundleService =
-      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIStringBundle> bundle;
-    rv = bundleService->CreateBundle(TRIDENTPROFILE_BUNDLE,
-                                     getter_AddRefs(bundle));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRInt64 keywordsFolder = 0;
-    int offset = 0;
-    while (1) {
-      nsAutoString keyName;
-      if (NS_FAILED(regKey->GetChildName(offset, keyName)))
-        break;
-
-      if (!keywordsFolder) {
-        nsString sourceNameIE;
-        rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameIE").get(),
-                                       getter_Copies(sourceNameIE));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        const PRUnichar* sourceNameStrings[] = { sourceNameIE.get() };
-        nsString importedIESearchUrlsTitle;
-        rv = bundle->FormatStringFromName(NS_LITERAL_STRING("importedSearchURLsFolder").get(),
-                                          sourceNameStrings, 1,
-                                          getter_Copies(importedIESearchUrlsTitle));
-        NS_ENSURE_SUCCESS(rv, rv);
-        rv = aBMS->CreateFolder(aParentFolder,
-                                NS_ConvertUTF16toUTF8(importedIESearchUrlsTitle),
-                                nsINavBookmarksService::DEFAULT_INDEX,
-                                &keywordsFolder);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-
-      nsCOMPtr<nsIWindowsRegKey> childKey; 
-      if (NS_SUCCEEDED(regKey->OpenChild(keyName,
-                       nsIWindowsRegKey::ACCESS_READ,
-                       getter_AddRefs(childKey)))) {
-        nsAutoString url; 
-        if (NS_SUCCEEDED(childKey->ReadStringValue(EmptyString(), url))) {
-          nsCOMPtr<nsIURI> uri;
-          if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), url))) {
-            NS_WARNING("Invalid url while importing smart keywords of MS IE");
-            ++offset;
-            childKey->Close();
-            continue;
-          }
-          PRInt64 id;
-          rv = aBMS->InsertBookmark(keywordsFolder, uri,
-                                    nsINavBookmarksService::DEFAULT_INDEX,
-                                    NS_ConvertUTF16toUTF8(keyName),
-                                    &id);
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-        childKey->Close();
-      }
-
-      ++offset;
-    }
-  }
-
-  return NS_OK;
-}
-
-void 
-nsIEProfileMigrator::ResolveShortcut(const nsString &aFileName, char** aOutURL) 
-{
-  HRESULT result;
-
-  IUniformResourceLocatorW* urlLink = nsnull;
-  result = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
-                              IID_IUniformResourceLocatorW, (void**)&urlLink);
-  if (SUCCEEDED(result) && urlLink) {
-    IPersistFile* urlFile = nsnull;
-    result = urlLink->QueryInterface(IID_IPersistFile, (void**)&urlFile);
-    if (SUCCEEDED(result) && urlFile) {
-      result = urlFile->Load(aFileName.get(), STGM_READ);
-      if (SUCCEEDED(result) ) {
-        LPWSTR lpTemp = nsnull;
-        result = urlLink->GetURL(&lpTemp);
-        if (SUCCEEDED(result) && lpTemp) {
-          *aOutURL = (char*)ToNewUTF8String(nsDependentString(lpTemp));
-          // free the string that GetURL alloc'd
-          ::CoTaskMemFree(lpTemp);
-        }
-      }
-      urlFile->Release();
-    }
-    urlLink->Release();
-  }
-}
-
-nsresult
-nsIEProfileMigrator::ParseFavoritesFolder(nsIFile* aDirectory, 
-                                          PRInt64 aParentFolder,
-                                          nsINavBookmarksService* aBMS,
-                                          const nsAString& aPersonalToolbarFolderName,
-                                          bool aIsAtRootLevel)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  do {
-    bool hasMore = false;
-    rv = entries->HasMoreElements(&hasMore);
-    if (NS_FAILED(rv) || !hasMore) break;
-
-    nsCOMPtr<nsISupports> supp;
-    rv = entries->GetNext(getter_AddRefs(supp));
-    if (NS_FAILED(rv)) break;
-
-    nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
-
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewFileURI(getter_AddRefs(uri), currFile);
-    if (NS_FAILED(rv)) break;
-
-    nsAutoString bookmarkName;
-    currFile->GetLeafName(bookmarkName);
-
-    bool isSymlink = false;
-    bool isDir = false;
-
-    currFile->IsSymlink(&isSymlink);
-    currFile->IsDirectory(&isDir);
-
-    if (isSymlink) {
-      // It's a .lnk file.  Get the path and check to see if it's
-      // a dir.  If so, create a bookmark for the dir.  If not, then
-      // simply do nothing and continue.
-
-      // Get the path that the .lnk file is pointing to.
-      nsAutoString path;
-      rv = currFile->GetTarget(path);
-      if (NS_FAILED(rv)) continue;
-
-      nsCOMPtr<nsILocalFile> localFile;
-      rv = NS_NewLocalFile(path, true, getter_AddRefs(localFile));
-      if (NS_FAILED(rv)) continue;
-
-      // Check for dir here.  If path is not a dir, just continue with
-      // next import.
-      rv = localFile->IsDirectory(&isDir);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (!isDir) continue;
-
-      // Look for and strip out the .lnk extension.
-      NS_NAMED_LITERAL_STRING(lnkExt, ".lnk");
-      PRInt32 lnkExtStart = bookmarkName.Length() - lnkExt.Length();
-      if (StringEndsWith(bookmarkName, lnkExt,
-                         CaseInsensitiveCompare))
-        bookmarkName.SetLength(lnkExtStart);
-
-      nsCOMPtr<nsIURI> bookmarkURI;
-      rv = NS_NewFileURI(getter_AddRefs(bookmarkURI), localFile);
-      if (NS_FAILED(rv)) continue;
-      PRInt64 id;
-      rv = aBMS->InsertBookmark(aParentFolder, bookmarkURI,
-                                nsINavBookmarksService::DEFAULT_INDEX,
-                                NS_ConvertUTF16toUTF8(bookmarkName),
-                                &id);
-      if (NS_FAILED(rv)) continue;
-    }
-    else if (isDir) {
-      PRInt64 folderId;
-      if (bookmarkName.Equals(aPersonalToolbarFolderName)) {
-        rv = aBMS->GetToolbarFolder(&folderId);
-        if (NS_FAILED(rv)) break;
-      }
-      else {
-        rv = aBMS->CreateFolder(aParentFolder,
-                                NS_ConvertUTF16toUTF8(bookmarkName),
-                                nsINavBookmarksService::DEFAULT_INDEX,
-                                &folderId);
-        if (NS_FAILED(rv)) continue;
-      }
-
-      rv = ParseFavoritesFolder(currFile, folderId,
-                                aBMS, aPersonalToolbarFolderName,
-                                false);
-      if (NS_FAILED(rv)) continue;
-    }
-    else {
-      nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
-      nsCAutoString extension;
-
-      url->GetFileExtension(extension);
-      if (!extension.Equals("url", CaseInsensitiveCompare))
-        continue;
-
-      nsAutoString name(Substring(bookmarkName, 0, 
-                                  bookmarkName.Length() - extension.Length() - 1));
-
-      nsAutoString path;
-      currFile->GetPath(path);
-
-      nsCString resolvedURL;
-      ResolveShortcut(path, getter_Copies(resolvedURL));
-
-      nsCOMPtr<nsIURI> resolvedURI;
-      rv = NS_NewURI(getter_AddRefs(resolvedURI), resolvedURL);
-      if (NS_FAILED(rv)) continue;
-      PRInt64 id;
-      rv = aBMS->InsertBookmark(aParentFolder, resolvedURI,
-                                nsINavBookmarksService::DEFAULT_INDEX,
-                                NS_ConvertUTF16toUTF8(name), &id);
-      if (NS_FAILED(rv)) continue;
-    }
-  }
-  while (1);
-
-  return rv;
-}
-
-nsresult
-nsIEProfileMigrator::CopyPreferences(bool aReplace) 
-{
-  bool            regKeyOpen = false;
-  const regEntry  *entry,
-                  *endEntry = ArrayEnd(gRegEntries);
-                              
-
-  nsCOMPtr<nsIPrefBranch> prefs;
-
-  { // scope pserve why not
-    nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
-    if (pserve)
-      pserve->GetBranch("", getter_AddRefs(prefs));
-  }
-  if (!prefs)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (!regKey)
-    return NS_ERROR_UNEXPECTED;
-  
-  // step through gRegEntries table
-  for (entry = gRegEntries; entry < endEntry; ++entry) {
-
-    // a new keyname? close any previous one and open the new one
-    if (entry->regKeyName) {
-      if (regKeyOpen) {
-        regKey->Close();
-        regKeyOpen = false;
-      }
-      regKeyOpen = NS_SUCCEEDED(regKey->
-                                Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                NS_ConvertASCIItoUTF16(
-                                  nsDependentCString(entry->regKeyName)),
-                                nsIWindowsRegKey::ACCESS_READ));
-    }
-
-    if (regKeyOpen) 
-      // read registry data
-      entry->entryHandler(regKey,
-                          NS_ConvertASCIItoUTF16(
-                            nsDependentCString(entry->regValueName)),
-                          prefs, entry->prefKeyName);
-  }
-
-  nsresult rv = CopySecurityPrefs(prefs);
-  if (NS_FAILED(rv)) return rv;
-
-  rv = CopyProxyPreferences(prefs);
-  if (NS_FAILED(rv)) return rv;
-
-  return CopyStyleSheet(aReplace);
-}
-
-/* Fetch and translate the current user's cookies.
-   Return true if successful. */
-nsresult
-nsIEProfileMigrator::CopyCookies(bool aReplace) 
-{
-  // IE cookies are stored in files named <username>@domain[n].txt
-  // (in <username>'s Cookies folder. isn't the naming redundant?)
-  nsresult rv = NS_OK;
-
-  nsCOMPtr<nsIFile> cookiesDir;
-  nsCOMPtr<nsISimpleEnumerator> cookieFiles;
-
-  nsCOMPtr<nsICookieManager2> cookieManager(do_GetService(NS_COOKIEMANAGER_CONTRACTID));
-  if (!cookieManager)
-    return NS_ERROR_FAILURE;
-
-  // find the cookies directory
-  NS_GetSpecialDirectory(NS_WIN_COOKIES_DIR, getter_AddRefs(cookiesDir));
-  if (!cookiesDir)
-    return NS_ERROR_FAILURE;
-
-  // Check for Vista's UAC, if so, tack on a "Low" sub dir
-  nsCOMPtr<nsIWindowsRegKey> regKey =
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (regKey) {
-    NS_NAMED_LITERAL_STRING(regPath,"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System");
-    if (NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
-                                  regPath,
-                                  nsIWindowsRegKey::ACCESS_QUERY_VALUE))) {
-      PRUint32 value;
-      if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("EnableLUA"),
-                                    &value)) &&
-          value == 1) {
-          nsAutoString dir;
-          // For cases where we are running under protected mode, check
-          // cookiesDir for the Low sub directory. (Simpler than using
-          // process token calls to check our Vista integrity level.)
-          cookiesDir->GetLeafName(dir);
-          if (!dir.EqualsLiteral("Low"))
-            cookiesDir->Append(NS_LITERAL_STRING("Low"));
-      }
-    }
-  }
-
-  cookiesDir->GetDirectoryEntries(getter_AddRefs(cookieFiles));
-  if (!cookieFiles)
-    return NS_ERROR_FAILURE;
-
-  // fetch the current user's name from the environment
-  PRUnichar username[sUsernameLengthLimit+2];
-  ::GetEnvironmentVariableW(L"USERNAME", username,
-                            sizeof(username)/sizeof(PRUnichar));
-  username[sUsernameLengthLimit] = L'\0';
-  wcscat(username, L"@");
-  int usernameLength = wcslen(username);
-
-  // allocate a buffer into which to read each cookie file
-  char *fileContents = (char *) PR_Malloc(sInitialCookieBufferSize);
-  if (!fileContents)
-    return NS_ERROR_OUT_OF_MEMORY;
-  PRUint32 fileContentsSize = sInitialCookieBufferSize;
-
-  do { // for each file in the cookies directory
-    // get the next file
-    bool moreFiles = false;
-    if (NS_FAILED(cookieFiles->HasMoreElements(&moreFiles)) || !moreFiles)
-      break;
-
-    nsCOMPtr<nsISupports> supFile;
-    cookieFiles->GetNext(getter_AddRefs(supFile));
-    nsCOMPtr<nsIFile> cookieFile(do_QueryInterface(supFile));
-    if (!cookieFile) {
-      rv = NS_ERROR_FAILURE;
-      break; // unexpected! punt!
-    }
-
-    // is it a cookie file for the current user?
-    nsAutoString fileName;
-    cookieFile->GetLeafName(fileName);
-    const nsAString &fileOwner = Substring(fileName, 0, usernameLength);
-    if (!fileOwner.Equals(username, CaseInsensitiveCompare))
-      continue;
-
-    // ensure the contents buffer is large enough to hold the entire file
-    // plus one byte (see DelimitField())
-    PRInt64 llFileSize;
-    if (NS_FAILED(cookieFile->GetFileSize(&llFileSize)))
-      continue;
-
-    PRUint32 fileSize, readSize;
-    LL_L2UI(fileSize, llFileSize);
-    if (fileSize >= fileContentsSize) {
-      PR_Free(fileContents);
-      fileContents = (char *) PR_Malloc(fileSize+1);
-      if (!fileContents) {
-        rv = NS_ERROR_FAILURE;
-        break; // fatal error
-      }
-      fileContentsSize = fileSize;
-    }
-
-    // read the entire cookie file
-    PRFileDesc *fd;
-    nsCOMPtr<nsILocalFile> localCookieFile(do_QueryInterface(cookieFile));
-    if (localCookieFile &&
-        NS_SUCCEEDED(localCookieFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd))) {
-
-      readSize = PR_Read(fd, fileContents, fileSize);
-      PR_Close(fd);
-
-      if (fileSize == readSize) { // translate this file's cookies
-        nsresult onerv;
-        onerv = CopyCookiesFromBuffer(fileContents, readSize, cookieManager);
-        if (NS_FAILED(onerv))
-          rv = onerv;
-      }
-    }
-  } while(1);
-
-  if (fileContents)
-    PR_Free(fileContents);
-  return rv;
-}
-
-/* Fetch cookies from a single IE cookie file.
-   Return true if successful. */
-nsresult
-nsIEProfileMigrator::CopyCookiesFromBuffer(char *aBuffer,
-                                           PRUint32 aBufferLength,
-                                           nsICookieManager2 *aCookieManager) 
-{
-  nsresult  rv = NS_OK;
-
-  const char *bufferEnd = aBuffer + aBufferLength;
-  // cookie fields:
-  char    *name,
-          *value,
-          *host,
-          *path,
-          *flags,
-          *expirationDate1, *expirationDate2,
-          *creationDate1, *creationDate2,
-          *terminator;
-  int      flagsValue;
-  time_t   expirationDate,
-           creationDate;
-  char     hostCopy[sHostnameLengthLimit+1],
-          *hostCopyConstructor,
-          *hostCopyEnd = hostCopy + sHostnameLengthLimit;
-
-  do { // for each cookie in the buffer
-    DelimitField(&aBuffer, bufferEnd, &name);
-    DelimitField(&aBuffer, bufferEnd, &value);
-    DelimitField(&aBuffer, bufferEnd, &host);
-    DelimitField(&aBuffer, bufferEnd, &flags);
-    DelimitField(&aBuffer, bufferEnd, &expirationDate1);
-    DelimitField(&aBuffer, bufferEnd, &expirationDate2);
-    DelimitField(&aBuffer, bufferEnd, &creationDate1);
-    DelimitField(&aBuffer, bufferEnd, &creationDate2);
-    DelimitField(&aBuffer, bufferEnd, &terminator);
-
-    // it's a cookie if we got one of each
-    if (terminator >= bufferEnd)
-      break;
-
-    // IE stores deleted cookies with a zero-length value
-    if (*value == '\0')
-      continue;
-
-    // convert flags to an int, date numbers to useable dates
-    ::sscanf(flags, "%d", &flagsValue);
-    expirationDate = FileTimeToTimeT(expirationDate1, expirationDate2);
-    creationDate = FileTimeToTimeT(creationDate1, creationDate2);
-
-    // munge host, and separate host from path
-
-    hostCopyConstructor = hostCopy;
-
-    // first, with a non-null domain, assume it's what Mozilla considers
-    // a domain cookie. see bug 222343.
-    if (*host && *host != '.' && *host != '/')
-      *hostCopyConstructor++ = '.';
-
-    // copy the host part and leave path pointing to the path part
-    for (path = host; *path && *path != '/'; ++path)
-      ;
-    int hostLength = path - host;
-    if (hostLength > hostCopyEnd - hostCopyConstructor)
-      hostLength = hostCopyEnd - hostCopyConstructor;
-    PL_strncpy(hostCopyConstructor, host, hostLength);
-    hostCopyConstructor += hostLength;
-
-    *hostCopyConstructor = '\0';
-
-    nsDependentCString stringName(name),
-                       stringPath(path);
-
-    // delete any possible extant matching host cookie and
-    // check if we're dealing with an IPv4/IPv6 hostname.
-    bool isIPAddress = false;
-    if (hostCopy[0] == '.') {
-      aCookieManager->Remove(nsDependentCString(hostCopy+1),
-                             stringName, stringPath, false);
-      PRNetAddr addr;
-      if (PR_StringToNetAddr(hostCopy+1, &addr) == PR_SUCCESS)
-        isIPAddress = true;
-    }
-
-    nsresult onerv;
-    // Add() makes a new domain cookie
-    onerv = aCookieManager->Add(nsDependentCString(hostCopy + (isIPAddress ? 1 : 0)),
-                                stringPath,
-                                stringName,
-                                nsDependentCString(value),
-                                flagsValue & 0x1, // isSecure
-                                false, // isHttpOnly
-                                false, // isSession
-                                PRInt64(expirationDate));
-    if (NS_FAILED(onerv)) {
-      rv = onerv;
-      break;
-    }
-
-  } while(aBuffer < bufferEnd);
-
-  return rv;
-}
-
-/* Delimit the next field in the IE cookie buffer.
-   when called:
-    aBuffer: at the beginning of the next field or preceding whitespace
-    aBufferEnd: one past the last valid character in the buffer
-   on return:
-    aField: at the beginning of the next field, which is null delimited
-    aBuffer: after the null at the end of the field
-
-   the character at which aBufferEnd points must be part of the buffer
-   so we can set it to \0.
-*/
-void
-nsIEProfileMigrator::DelimitField(char **aBuffer,
-                                  const char *aBufferEnd,
-                                  char **aField) 
-{
-  char *scan = *aBuffer;
-  *aField = scan;
-  while (scan < aBufferEnd && (*scan != '\r' && *scan != '\n'))
-    ++scan;
-  if (scan+1 < aBufferEnd && (*(scan+1) == '\r' || *(scan+1) == '\n') &&
-                             *scan != *(scan+1)) {
-    *scan = '\0';
-    scan += 2;
-  } else {
-    if (scan <= aBufferEnd) // (1 byte past bufferEnd is guaranteed allocated)
-      *scan = '\0';
-    ++scan;
-  }
-  *aBuffer = scan;
-}
-
-// conversion routine. returns 0 (epoch date) if the input is out of range
-time_t
-nsIEProfileMigrator::FileTimeToTimeT(const char *aLowDateIntString,
-                                     const char *aHighDateIntString) 
-{
-  FILETIME   fileTime;
-  SYSTEMTIME systemTime;
-  tm         tTime;
-  time_t     rv;
-
-  ::sscanf(aLowDateIntString, "%ld", &fileTime.dwLowDateTime);
-  ::sscanf(aHighDateIntString, "%ld", &fileTime.dwHighDateTime);
-  ::FileTimeToSystemTime(&fileTime, &systemTime);
-  tTime.tm_year = systemTime.wYear - 1900;
-  tTime.tm_mon = systemTime.wMonth-1;
-  tTime.tm_mday = systemTime.wDay;
-  tTime.tm_hour = systemTime.wHour;
-  tTime.tm_min = systemTime.wMinute;
-  tTime.tm_sec = systemTime.wSecond;
-  tTime.tm_isdst = -1;
-  rv = ::mktime(&tTime);
-  return rv < 0 ? 0 : rv;
-}
-
-/* Find the accessibility stylesheet if it exists and replace Mozilla's
-   with it. Return true if we found and copied a stylesheet. */
-nsresult
-nsIEProfileMigrator::CopyStyleSheet(bool aReplace) 
-{
-  nsresult rv = NS_OK; // return failure only if filecopy fails
-
-  // is there a trident user stylesheet?
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (!regKey)
-    return NS_ERROR_UNEXPECTED;
-
-  NS_NAMED_LITERAL_STRING(styleKey,
-                          "Software\\Microsoft\\Internet Explorer\\Styles");
-  if (NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                             styleKey, nsIWindowsRegKey::ACCESS_READ)))
-    return NS_OK;
-
-  NS_NAMED_LITERAL_STRING(myStyleValName, "Use My StyleSheet");
-  PRUint32 type, useMyStyle; 
-  if (NS_SUCCEEDED(regKey->GetValueType(myStyleValName, &type)) &&
-      type == nsIWindowsRegKey::TYPE_INT &&
-      NS_SUCCEEDED(regKey->ReadIntValue(myStyleValName, &useMyStyle)) &&
-      useMyStyle == 1) {
-
-    nsAutoString tridentFilename;
-    if (NS_SUCCEEDED(regKey->ReadStringValue(
-                     NS_LITERAL_STRING("User Stylesheet"), tridentFilename))) {
-
-      // tridentFilename is a native path to the specified stylesheet file
-      // point an nsIFile at it
-      nsCOMPtr<nsILocalFile> tridentFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
-      if (tridentFile) {
-        bool exists;
-
-        tridentFile->InitWithPath(tridentFilename);
-        tridentFile->Exists(&exists);
-        if (exists) {
-          // now establish our file (userContent.css in the profile/chrome dir)
-          nsCOMPtr<nsIFile> chromeDir;
-          NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR,
-                                 getter_AddRefs(chromeDir));
-          if (chromeDir)
-            rv = tridentFile->CopyTo(chromeDir,
-                              NS_LITERAL_STRING("userContent.css"));
-        }
-      }
-    }
-  }
-  return rv;
-}
-
-void
-nsIEProfileMigrator::GetUserStyleSheetFile(nsIFile **aUserFile) 
-{
-  nsCOMPtr<nsIFile> userChrome;
-
-  *aUserFile = 0;
-
-  // establish the chrome directory
-  NS_GetSpecialDirectory(NS_APP_USER_CHROME_DIR, getter_AddRefs(userChrome));
-
-  if (userChrome) {
-    bool exists;
-    userChrome->Exists(&exists);
-    if (!exists &&
-        NS_FAILED(userChrome->Create(nsIFile::DIRECTORY_TYPE, 0755)))
-      return;
-
-    // establish the user content stylesheet file
-    userChrome->Append(NS_LITERAL_STRING("userContent.css"));
-    *aUserFile = userChrome;
-    NS_ADDREF(*aUserFile);
-  }
-}
-
-nsresult
-nsIEProfileMigrator::CopySecurityPrefs(nsIPrefBranch* aPrefs)
-{
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  NS_NAMED_LITERAL_STRING(key,
-      "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
-  if (regKey && 
-      NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                key, nsIWindowsRegKey::ACCESS_READ))) {
-    
-    PRUint32 value;
-    if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("SecureProtocols"),
-                                          &value))) { 
-      aPrefs->SetBoolPref("security.enable_ssl3", (value >> 5) & true);
-      aPrefs->SetBoolPref("security.enable_tls",  (value >> 7) & true);
-    }
-  }
-
-  return NS_OK;
-}
-
-struct ProxyData {
-  char*   prefix;
-  PRInt32 prefixLength;
-  bool    proxyConfigured;
-  char*   hostPref;
-  char*   portPref;
-};
-
-nsresult
-nsIEProfileMigrator::CopyProxyPreferences(nsIPrefBranch* aPrefs)
-{
-  nsCOMPtr<nsIWindowsRegKey> regKey =
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  NS_NAMED_LITERAL_STRING(key,
-    "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
-  if (regKey && 
-      NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
-                                key, nsIWindowsRegKey::ACCESS_READ))) {
-    nsAutoString buf; 
-
-    PRUint32 proxyType = 0;
-    // If there's an autoconfig URL specified in the registry at all, 
-    // it is being used. 
-    if (NS_SUCCEEDED(regKey->
-                     ReadStringValue(NS_LITERAL_STRING("AutoConfigURL"), buf))) {
-      // make this future-proof (MS IE will support IDN eventually and
-      // 'URL' will contain more than ASCII characters)
-      SetUnicharPref("network.proxy.autoconfig_url", buf, aPrefs);
-      proxyType = 2;
-    }
-
-    // ProxyEnable
-    PRUint32 enabled;
-    if (NS_SUCCEEDED(regKey->
-                     ReadIntValue(NS_LITERAL_STRING("ProxyEnable"), &enabled))) {
-      if (enabled & 0x1)
-        proxyType = 1;
-    }
-    
-    aPrefs->SetIntPref("network.proxy.type", proxyType); 
-    
-    if (NS_SUCCEEDED(regKey->
-                     ReadStringValue(NS_LITERAL_STRING("ProxyOverride"), buf)))
-      ParseOverrideServers(buf, aPrefs);
-
-    if (NS_SUCCEEDED(regKey->
-                     ReadStringValue(NS_LITERAL_STRING("ProxyServer"), buf))) {
-
-      ProxyData data[] = {
-        { "ftp=",     4, false, "network.proxy.ftp",
-          "network.proxy.ftp_port"    },
-        { "http=",    5, false, "network.proxy.http",
-          "network.proxy.http_port"   },
-        { "https=",   6, false, "network.proxy.ssl",
-          "network.proxy.ssl_port"    },
-        { "socks=",   6, false, "network.proxy.socks",
-          "network.proxy.socks_port"  },
-      };
-
-      PRInt32 startIndex = 0, count = 0;
-      bool foundSpecificProxy = false;
-      for (PRUint32 i = 0; i < ArrayLength(data); ++i) {
-        PRInt32 offset = buf.Find(NS_ConvertASCIItoUTF16(data[i].prefix));
-        if (offset >= 0) {
-          foundSpecificProxy = true;
-
-          data[i].proxyConfigured = true;
-
-          startIndex = offset + data[i].prefixLength;
-
-          PRInt32 terminal = buf.FindChar(';', offset);
-          count = terminal > startIndex ? terminal - startIndex : 
-                                          buf.Length() - startIndex;
-
-          // hostPort now contains host:port
-          SetProxyPref(Substring(buf, startIndex, count), data[i].hostPref,
-                       data[i].portPref, aPrefs);
-        }
-      }
-
-      if (!foundSpecificProxy) {
-        // No proxy config for any specific type was found, assume 
-        // the ProxyServer value is of the form host:port and that 
-        // it applies to all protocols.
-        for (PRUint32 i = 0; i < ArrayLength(data); ++i)
-          SetProxyPref(buf, data[i].hostPref, data[i].portPref, aPrefs);
-        aPrefs->SetBoolPref("network.proxy.share_proxy_settings", true);
-      }
-    }
-
-  }
-
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/browser/components/migration/src/nsIEProfileMigrator.h
+++ /dev/null
@@ -1,208 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* Private header describing the class to migrate preferences from
-   Windows Trident to Gecko. This is a virtual class. */
-
-#ifndef ieprofilemigrator___h___
-#define ieprofilemigrator___h___
-
-#include <time.h>
-#include <windows.h>
-#include <ole2.h>
-#include "nsIBrowserProfileMigrator.h"
-#include "nsIObserverService.h"
-#include "nsTArray.h"
-#include "nsINavHistoryService.h"
-
-class nsIFile;
-class nsICookieManager2;
-class nsIRDFResource;
-class nsINavBookmarksService;
-class nsIPrefBranch;
-
-struct SignonData {
-  PRUnichar* user;
-  PRUnichar* pass;
-  char*      realm;
-};
-
-// VC11 doesn't ship with pstore.h, so we go ahead and define the stuff that
-// we need from that file here.
-class IEnumPStoreItems : public IUnknown {
-public:
-  virtual HRESULT STDMETHODCALLTYPE Next(DWORD celt, LPWSTR* rgelt,
-                                         DWORD* pceltFetched) = 0;
-  virtual HRESULT STDMETHODCALLTYPE Skip(DWORD celt) = 0;
-  virtual HRESULT STDMETHODCALLTYPE Reset() = 0;
-  virtual HRESULT STDMETHODCALLTYPE Clone(IEnumPStoreItems** ppenum) = 0;
-};
-
-class IEnumPStoreTypes; // not used
-struct PST_PROVIDERINFO; // not used
-struct PST_TYPEINFO; // not used
-struct PST_PROMPTINFO; // not used
-struct PST_ACCESSRULESET; // not used
-typedef DWORD PST_KEY;
-typedef DWORD PST_ACCESSMODE;
-
-class IPStore : public IUnknown {
-public:
-  virtual HRESULT STDMETHODCALLTYPE GetInfo(PST_PROVIDERINFO** ppProperties) = 0;
-  virtual HRESULT STDMETHODCALLTYPE GetProvParam(DWORD dwParam, DWORD* pcbData,
-                                                 BYTE** ppbData, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE SetProvParam(DWORD dwParam, DWORD cbData,
-                                                 BYTE* pbData, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE CreateType(PST_KEY Key, const GUID* pType,
-                                               PST_TYPEINFO* pInfo, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(PST_KEY Key, const GUID* pType,
-                                                PST_TYPEINFO** ppInfo, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE DeleteType(PST_KEY Key, const GUID* pType,
-                                               DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE CreateSubtype(PST_KEY Key, const GUID* pType,
-                                                  const GUID* pSubtype, PST_TYPEINFO* pInfo,
-                                                  PST_ACCESSRULESET* pRules, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE GetSubtypeInfo(PST_KEY Key, const GUID* pType,
-                                                   const GUID* pSubtype, PST_TYPEINFO** ppInfo,
-                                                   DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE DeleteSubtype(PST_KEY Key, const GUID* pType,
-                                                  const GUID* pSubtype, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE ReadAccessRuleset(PST_KEY Key, const GUID* pType,
-                                                      const GUID* pSubtype, PST_ACCESSRULESET** ppRules,
-                                                      DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE WriteAccessRuleset(PST_KEY Key, const GUID* pType,
-                                                       const GUID* pSubtype, PST_ACCESSRULESET* pRules,
-                                                       DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE EnumTypes(PST_KEY Key, DWORD dwFlags, IEnumPStoreTypes** ppenum) = 0;
-  virtual HRESULT STDMETHODCALLTYPE EnumSubtypes(PST_KEY Key, const GUID* pType,
-                                                 DWORD dwFlags, IEnumPStoreTypes** ppenum) = 0;
-  virtual HRESULT STDMETHODCALLTYPE DeleteItem(PST_KEY Key, const GUID* pItemType,
-                                               const GUID* pItemSubtype, LPCWSTR szItemName,
-                                               PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE ReadItem(PST_KEY Key, const GUID* pItemType,
-                                             const GUID* pItemSubtype, LPCWSTR szItemName,
-                                             DWORD* pcbData, BYTE** ppbData,
-                                             PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE WriteItem(PST_KEY Key, const GUID* pItemType,
-                                              const GUID* pItemSubtype, LPCWSTR szItemName,
-                                              DWORD cbData, BYTE* pbData,
-                                              PST_PROMPTINFO* pPromptInfo, DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE OpenItem(PST_KEY Key, const GUID* pItemType,
-                                             const GUID* pItemSubtype, LPCWSTR szItemName,
-                                             PST_ACCESSMODE ModeFlags, PST_PROMPTINFO* pPromptInfo,
-                                             DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE CloseItem(PST_KEY Key, const GUID* pItemType,
-                                              const GUID* pItemSubtype, LPCWSTR szItemName,
-                                              DWORD dwFlags) = 0;
-  virtual HRESULT STDMETHODCALLTYPE EnumItems(PST_KEY Key, const GUID* pItemType,
-                                              const GUID* pItemSubtype, DWORD dwFlags,
-                                              IEnumPStoreItems** ppenum) = 0;
-};
-
-
-class nsIEProfileMigrator : public nsIBrowserProfileMigrator,
-                            public nsINavHistoryBatchCallback {
-public:
-  NS_DECL_NSIBROWSERPROFILEMIGRATOR
-  NS_DECL_NSINAVHISTORYBATCHCALLBACK
-  NS_DECL_ISUPPORTS
-
-  nsIEProfileMigrator();
-  virtual ~nsIEProfileMigrator();
-
-protected:
-  nsresult CopyPreferences(bool aReplace);
-  nsresult CopyStyleSheet(bool aReplace);
-  nsresult CopyCookies(bool aReplace);
-  nsresult CopyProxyPreferences(nsIPrefBranch* aPrefs);
-  nsresult CopySecurityPrefs(nsIPrefBranch* aPrefs);
-  /**
-   * Migrate history to Places.
-   * This will end up calling CopyHistoryBatched helper, that provides batch
-   * support.  Batching allows for better performances and integrity.
-   *
-   * @param aReplace
-   *        Indicates if we should replace current history or append to it.
-   */
-  nsresult CopyHistory(bool aReplace);
-  nsresult CopyHistoryBatched(bool aReplace);
-
-  bool     KeyIsURI(const nsAString& aKey, char** aRealm);
-
-  nsresult CopyPasswords(bool aReplace);
-  nsresult MigrateSiteAuthSignons(IPStore* aPStore);
-  nsresult GetSignonsListFromPStore(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound);
-  nsresult ResolveAndMigrateSignons(IPStore* aPStore, nsTArray<SignonData>* aSignonsFound);
-  void     EnumerateUsernames(const nsAString& aKey, PRUnichar* aData, unsigned long aCount, nsTArray<SignonData>* aSignonsFound);
-  void     GetUserNameAndPass(unsigned char* data, unsigned long len, unsigned char** username, unsigned char** pass);
-
-  nsresult CopyFormData(bool aReplace);
-  nsresult AddDataToFormHistory(const nsAString& aKey, PRUnichar* data, unsigned long len);
-  /**
-   * Migrate bookmarks to Places.
-   * This will end up calling CopyFavoritesBatched helper, that provides batch
-   * support.  Batching allows for better performances and integrity.
-   *
-   * @param aReplace
-   *        Indicates if we should replace current bookmarks or append to them.
-   *        When appending we will usually default to bookmarks menu.
-   */
-  nsresult CopyFavorites(bool aReplace);
-  nsresult CopyFavoritesBatched(bool aReplace);
-  void     ResolveShortcut(const nsString &aFileName, char** aOutURL);
-  nsresult ParseFavoritesFolder(nsIFile* aDirectory, 
-                                PRInt64 aParentFolder,
-                                nsINavBookmarksService* aBookmarksService,
-                                const nsAString& aPersonalToolbarFolderName,
-                                bool aIsAtRootLevel);
-  nsresult CopySmartKeywords(nsINavBookmarksService* aBMS,
-                             PRInt64 aParentFolder);
-
-  nsresult CopyCookiesFromBuffer(char *aBuffer, PRUint32 aBufferLength,
-                                 nsICookieManager2 *aCookieManager);
-  void     DelimitField(char **aBuffer, const char *aBufferEnd, char **aField);
-  time_t   FileTimeToTimeT(const char *aLowDateIntString,
-                           const char *aHighDateIntString);
-  void     GetUserStyleSheetFile(nsIFile **aUserFile);
-  bool     TestForIE7();
-
-private:
-  nsCOMPtr<nsIObserverService> mObserverService;
-};
-
-#endif
-
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 head = head_migration.js
 tail =
 
 [test_IE_bookmarks.js]
-skip-if = os != "win"
+skip-if = true
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -33,16 +33,20 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
+                                  "resource:///modules/DownloadsCommon.jsm");
+
 var gMainPane = {
   _pane: null,
 
   /**
    * Initialization of this.
    */
   init: function ()
   {
@@ -50,22 +54,35 @@ var gMainPane = {
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
 
     this.updateBrowserStartupLastSession();
     this.startupPagePrefChanged();
 
+    this.setupDownloadsWindowOptions();
+
     // Notify observers that the UI is now ready
     Components.classes["@mozilla.org/observer-service;1"]
               .getService(Components.interfaces.nsIObserverService)
               .notifyObservers(window, "main-pane-loaded", null);
   },
 
+  setupDownloadsWindowOptions: function ()
+  {
+    var showWhenDownloading = document.getElementById("showWhenDownloading");
+    var closeWhenDone = document.getElementById("closeWhenDone");
+
+    // These radio-buttons should not be visible if we have enabled the Downloads Panel.
+    let shouldHide = !DownloadsCommon.useToolkitUI;
+    showWhenDownloading.hidden = shouldHide;
+    closeWhenDone.hidden = shouldHide;
+  },
+
   // HOME PAGE
 
   /*
    * Preferences:
    *
    * browser.startup.homepage
    * - the user's home page, as a string; if the home page is a set of tabs,
    *   this will be those URLs separated by the pipe character "|"
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.manifest
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.manifest
@@ -1,4 +1,17 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {c31f4883-839b-45f6-82ad-a6a9bc5ad599} nsPrivateBrowsingService.js
 contract @mozilla.org/privatebrowsing;1 {c31f4883-839b-45f6-82ad-a6a9bc5ad599}
 category command-line-handler m-privatebrowsing @mozilla.org/privatebrowsing;1
-category app-startup nsPrivateBrowsingService service,@mozilla.org/privatebrowsing;1
+category app-startup nsPrivateBrowsingService service,@mozilla.org/privatebrowsing;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/components/sessionstore/src/nsSessionStore.manifest
+++ b/browser/components/sessionstore/src/nsSessionStore.manifest
@@ -1,5 +1,18 @@
+# WebappRT doesn't need these instructions, and they don't necessarily work
+# with it, but it does use a GRE directory that the GRE shares with Firefox,
+# so in order to prevent the instructions from being processed for WebappRT,
+# we need to restrict them to the applications that depend on them, i.e.:
+#
+#   b2g:            {3c2e2abc-06d4-11e1-ac3b-374f68613e61}
+#   browser:        {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+#   mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
+#   mobile/xul:     {a23983c0-fd0e-11dc-95ff-0800200c9a66}
+#
+# In theory we should do this for all these instructions, but in practice it is
+# sufficient to do it for the app-startup one, and the file is simpler that way.
+
 component {5280606b-2510-4fe0-97ef-9b5a22eafe6b} nsSessionStore.js
 contract @mozilla.org/browser/sessionstore;1 {5280606b-2510-4fe0-97ef-9b5a22eafe6b}
 component {ec7a6c20-e081-11da-8ad9-0800200c9a66} nsSessionStartup.js
 contract @mozilla.org/browser/sessionstartup;1 {ec7a6c20-e081-11da-8ad9-0800200c9a66}
-category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1
+category app-startup nsSessionStartup service,@mozilla.org/browser/sessionstartup;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66}
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
@@ -25,18 +25,16 @@ let TestObserver = {
       return;
     }
 
     is(aSubject.category, "Web Socket", "received a Web Socket error");
     isnot(aSubject.sourceName.indexOf("test-bug-603750-websocket.js"), -1,
           "sourceName is correct");
 
     if (++errors == 2) {
-      is(lastWindowId, aSubject.outerWindowID,
-         "same window ID (" + lastWindowId + ") for both errors");
       executeSoon(performTest);
     }
     else {
       lastWindowId = aSubject.outerWindowID;
     }
   }
 };
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -303,16 +303,19 @@
 @BINPATH@/components/fuelApplication.manifest
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
+@BINPATH@/components/BrowserDownloads.manifest
+@BINPATH@/components/DownloadsStartup.js
+@BINPATH@/components/DownloadsUI.js
 @BINPATH@/components/BrowserPlaces.manifest
 @BINPATH@/components/BrowserPageThumbs.manifest
 @BINPATH@/components/nsPrivateBrowsingService.manifest
 @BINPATH@/components/nsPrivateBrowsingService.js
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
 @BINPATH@/components/passwordmgr.manifest
@@ -404,16 +407,17 @@
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 #endif
 @BINPATH@/components/BrowserProfileMigrators.manifest
 @BINPATH@/components/ProfileMigrator.js
 @BINPATH@/components/ChromeProfileMigrator.js
 @BINPATH@/components/FirefoxProfileMigrator.js
 #ifdef XP_WIN
+@BINPATH@/components/IEProfileMigrator.js
 @BINPATH@/components/SafariProfileMigrator.js
 #endif
 #ifdef XP_MACOSX
 @BINPATH@/components/SafariProfileMigrator.js
 @BINPATH@/components/libalerts.dylib
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
@@ -494,16 +498,19 @@
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @BINPATH@/@PREF_DIR@/firefox.js
 @BINPATH@/@PREF_DIR@/firefox-branding.js
 @BINPATH@/@PREF_DIR@/channel-prefs.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/@PREF_DIR@/services-sync.js
 #endif
+#ifdef MOZ_WEBAPP_RUNTIME
+@BINPATH@/@PREF_DIR@/webapprt@mozilla.org/prefs.js
+#endif
 @BINPATH@/greprefs.js
 @BINPATH@/defaults/autoconfig/platform.js
 @BINPATH@/defaults/autoconfig/prefcalls.js
 @BINPATH@/defaults/profile/prefs.js
 
 ; [Layout Engine Resources]
 ; Style Sheets, Graphics and other Resources used by the layout engine. 
 @BINPATH@/res/EditorOverride.css
@@ -615,8 +622,19 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #endif
 
 ; [OS/2]
 #ifdef XP_OS2
 @BINPATH@/MozSounds.cmd
 @BINPATH@/*.xqs
 @BINPATH@/components/*.xqs
 #endif
+
+#ifdef MOZ_WEBAPP_RUNTIME
+; [Webapp Runtime]
+@BINPATH@/webapprt-stub@BIN_SUFFIX@
+@BINPATH@/chrome/webapprt@JAREXT@
+@BINPATH@/chrome/webapprt.manifest
+@BINPATH@/components/WebappRTComponents.manifest
+@BINPATH@/components/WebappRTDirectoryProvider.js
+@BINPATH@/components/WebappRTCommandLineHandler.js
+@BINPATH@/webapprt.ini
+#endif
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.dtd
@@ -0,0 +1,41 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
+   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
+
+<!-- LOCALIZATION NOTE (indicator.tooltiptext):
+     Tooltip for the indicator that displays the progress of ongoing downloads.
+     -->
+<!ENTITY indicator.tooltiptext            "Downloads">
+
+<!-- LOCALIZATION NOTE (downloads.title):
+     Used by screen readers to describe the Downloads Panel.
+     -->
+<!ENTITY downloads.title                  "Downloads">
+
+<!ENTITY cmd.pause.label                  "Pause">
+<!ENTITY cmd.pause.accesskey              "P">
+<!ENTITY cmd.resume.label                 "Resume">
+<!ENTITY cmd.resume.accesskey             "R">
+<!ENTITY cmd.cancel.label                 "Cancel">
+<!ENTITY cmd.cancel.accesskey             "C">
+<!-- LOCALIZATION NOTE (cmd.show.label, cmd.show.accesskey, cmd.showMac.label,
+     cmd.showMac.accesskey):
+     The show and showMac commands are never shown together, thus they can share
+     the same access key (though the two access keys can also be different).
+     -->
+<!ENTITY cmd.show.label                   "Open Containing Folder">
+<!ENTITY cmd.show.accesskey               "F">
+<!ENTITY cmd.showMac.label                "Show In Finder">
+<!ENTITY cmd.showMac.accesskey            "F">
+<!ENTITY cmd.retry.label                  "Retry">
+<!ENTITY cmd.goToDownloadPage.label       "Go To Download Page">
+<!ENTITY cmd.goToDownloadPage.accesskey   "G">
+<!ENTITY cmd.copyDownloadLink.label       "Copy Download Link">
+<!ENTITY cmd.copyDownloadLink.accesskey   "L">
+<!ENTITY cmd.removeFromList.label         "Remove From List">
+<!ENTITY cmd.removeFromList.accesskey     "e">
+<!ENTITY cmd.clearList.label              "Clear List">
+<!ENTITY cmd.clearList.accesskey          "a">
+
+<!ENTITY downloadshistory.label           "Show All Downloads">
+<!ENTITY downloadshistory.accesskey       "S">
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.properties
@@ -0,0 +1,72 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# LOCALIZATION NOTE (stateStarting):
+# Indicates that the download is starting.
+stateStarting=Starting…
+# LOCALIZATION NOTE (stateScanning):
+# Indicates that an external program is scanning the download for viruses.
+stateScanning=Scanning for viruses…
+# LOCALIZATION NOTE (stateFailed):
+# Indicates that the download failed because of an error.
+stateFailed=Failed
+# LOCALIZATION NOTE (statePaused):
+# Indicates that the download was paused by the user.
+statePaused=Paused
+# LOCALIZATION NOTE (stateCanceled):
+# Indicates that the download was canceled by the user.
+stateCanceled=Canceled
+# LOCALIZATION NOTE (stateBlockedParentalControls):
+# Indicates that the download was blocked by the Parental Controls feature of
+# Windows.  "Parental Controls" should be consistently named and capitalized
+# with the display of this feature in Windows.  The following article can
+# provide a reference for the translation of "Parental Controls" in various
+# languages:
+# http://windows.microsoft.com/en-US/windows-vista/Set-up-Parental-Controls
+stateBlockedParentalControls=Blocked by Parental Controls
+# LOCALIZATION NOTE (stateBlockedPolicy):
+# Indicates that the download was blocked on Windows because of the "Launching
+# applications and unsafe files" setting of the "security zone" associated with
+# the target site.  "Security zone" should be consistently named and capitalized
+# with the display of this feature in Windows.  The following article can
+# provide a reference for the translation of "security zone" in various
+# languages:
+# http://support.microsoft.com/kb/174360
+stateBlockedPolicy=Blocked by your security zone policy
+# LOCALIZATION NOTE (stateDirty):
+# Indicates that the download was blocked after scanning.
+stateDirty=Blocked: May contain a virus or spyware
+
+# LOCALIZATION NOTE (sizeWithUnits):
+# %1$S is replaced with the size number, and %2$S with the measurement unit.
+sizeWithUnits=%1$S %2$S
+sizeUnknown=Unknown size
+
+# LOCALIZATION NOTE (shortTimeLeftSeconds, shortTimeLeftMinutes,
+# shortTimeLeftHours, shortTimeLeftDays):
+# These values are displayed in the downloads indicator in the main browser
+# window, where space is available for three characters maximum.  %1$S is
+# replaced with the time left for the given measurement unit.  Even for days,
+# the value is never longer than two digits.
+shortTimeLeftSeconds=%1$Ss
+shortTimeLeftMinutes=%1$Sm
+shortTimeLeftHours=%1$Sh
+shortTimeLeftDays=%1$Sd
+
+# LOCALIZATION NOTE (statusSeparator, statusSeparatorBeforeNumber):
+# These strings define templates for the separation of different elements in the
+# status line of a download item.  As a separator, by default we use the Unicode
+# character U+2014 'EM DASH' (long dash).  Examples of status lines include
+# "Canceled - 222.net", "1.1 MB - website2.com", or "Paused -  1.1 MB".  Note
+# that we use a wider space after the separator when it is followed by a number,
+# just to avoid visually confusing it with with a minus sign with some fonts.
+# If you use a different separator, this might not be necessary.  However, there
+# is usually no need to change the separator or the order of the substitutions,
+# even for right-to-left languages, unless the defaults are not suitable.
+statusSeparator=%1$S \u2014 %2$S
+statusSeparatorBeforeNumber=%1$S \u2014  %2$S
+
+fileExecutableSecurityWarning="%S" is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch "%S"?
+fileExecutableSecurityWarningTitle=Open Executable File?
+fileExecutableSecurityWarningDontAsk=Don't ask me this again
--- a/browser/locales/en-US/chrome/browser/migration/migration.properties
+++ b/browser/locales/en-US/chrome/browser/migration/migration.properties
@@ -2,19 +2,16 @@ profileName_format=%S %S
 
 # Browser Specific
 sourceNameIE=Internet Explorer
 sourceNameSafari=Safari
 sourceNameChrome=Google Chrome
 sourceNameFirefox=Mozilla Firefox
 
 importedBookmarksFolder=From %S
-importedSearchURLsFolder=Keyword Searches (From %S)
-importedSearchURLsTitle=Search on %S
-importedSearchUrlDesc=Type "%S <search query>" in the Location Bar to perform a search on %S.
 
 importedSafariReadingList=Reading List (From Safari)
 
 # Import Sources
 1_ie=Internet Options
 1_safari=Preferences
 1_chrome=Preferences
 
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/webapprt/webapp.dtd
@@ -0,0 +1,49 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this file,
+   - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
+
+<!-- LOCALIZATION NOTE: These are localized strings for the webapp runtime,
+   - which loads a webapp in a separate process from Firefox.  Webapps loaded
+   - in this way have very little application chrome, but the runtime does
+   - provide them with some default functionality, like the standard OS
+   - menus/menuitems. -->
+
+<!ENTITY fileMenu.label                     "File">
+<!ENTITY fileMenu.accesskey                 "F">
+
+<!ENTITY quitApplicationCmdWin.label        "Exit">
+<!ENTITY quitApplicationCmdWin.accesskey    "x">
+<!ENTITY quitApplicationCmd.label           "Quit">
+<!ENTITY quitApplicationCmd.accesskey       "Q">
+<!-- On Mac, we create the Quit and Hide command labels dynamically,
+   - using properties in window.properties, in order to include the name
+   - of the webapp in the labels without creating a DTD file for it. -->
+<!ENTITY quitApplicationCmdMac.key          "Q">
+<!ENTITY hideThisAppCmdMac.key              "H">
+<!ENTITY hideOtherAppsCmdMac.label          "Hide Others">
+<!ENTITY hideOtherAppsCmdMac.key            "H">
+<!ENTITY showAllAppsCmdMac.label            "Show All">
+
+<!ENTITY editMenu.label                     "Edit">
+<!ENTITY editMenu.accesskey                 "E">
+<!ENTITY undoCmd.label                      "Undo">
+<!ENTITY undoCmd.key                        "Z">
+<!ENTITY undoCmd.accesskey                  "U">
+<!ENTITY redoCmd.label                      "Redo">
+<!ENTITY redoCmd.key                        "Y">
+<!ENTITY redoCmd.accesskey                  "R">
+<!ENTITY cutCmd.label                       "Cut">
+<!ENTITY cutCmd.key                         "X">
+<!ENTITY cutCmd.accesskey                   "t">
+<!ENTITY copyCmd.label                      "Copy">
+<!ENTITY copyCmd.key                        "C">
+<!ENTITY copyCmd.accesskey                  "C">
+<!ENTITY pasteCmd.label                     "Paste">
+<!ENTITY pasteCmd.key                       "V">
+<!ENTITY pasteCmd.accesskey                 "P">
+<!ENTITY deleteCmd.label                    "Delete">
+<!ENTITY deleteCmd.key                      "D">
+<!ENTITY deleteCmd.accesskey                "D">
+<!ENTITY selectAllCmd.label                 "Select All">
+<!ENTITY selectAllCmd.key                   "A">
+<!ENTITY selectAllCmd.accesskey             "A">
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/webapprt/webapp.properties
@@ -0,0 +1,17 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# LOCALIZATION NOTE: These are localized strings for the webapp runtime,
+# which loads a webapp in a separate process from Firefox.  Webapps loaded
+# in this way have very little application chrome, but the runtime does
+# provide them with some default functionality, like the standard OS
+# menus/menuitems.
+
+# LOCALIZATION NOTE (quitApplicationCmdMac.label): %S will be replaced with
+# the name of the webapp.
+quitApplicationCmdMac.label=Quit %S
+
+# LOCALIZATION NOTE (hideApplicationCmdMac.label): %S will be replaced with
+# the name of the webapp.
+hideApplicationCmdMac.label=Hide %S
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -45,16 +45,18 @@
     locale/browser/engineManager.dtd               (%chrome/browser/engineManager.dtd)
     locale/browser/engineManager.properties        (%chrome/browser/engineManager.properties)
     locale/browser/setDesktopBackground.dtd        (%chrome/browser/setDesktopBackground.dtd)
     locale/browser/shellservice.properties         (%chrome/browser/shellservice.properties)
     locale/browser/tabbrowser.dtd                  (%chrome/browser/tabbrowser.dtd)
     locale/browser/tabbrowser.properties           (%chrome/browser/tabbrowser.properties)
     locale/browser/tabview.properties              (%chrome/browser/tabview.properties)
     locale/browser/taskbar.properties              (%chrome/browser/taskbar.properties)
+    locale/browser/downloads/downloads.dtd         (%chrome/browser/downloads/downloads.dtd)
+    locale/browser/downloads/downloads.properties  (%chrome/browser/downloads/downloads.properties)
     locale/browser/places/places.dtd               (%chrome/browser/places/places.dtd)
     locale/browser/places/places.properties        (%chrome/browser/places/places.properties)
     locale/browser/places/editBookmarkOverlay.dtd  (%chrome/browser/places/editBookmarkOverlay.dtd)
     locale/browser/places/bookmarkProperties.properties (%chrome/browser/places/bookmarkProperties.properties)
     locale/browser/preferences/selectBookmark.dtd  (%chrome/browser/preferences/selectBookmark.dtd)
     locale/browser/places/moveBookmarks.dtd        (%chrome/browser/places/moveBookmarks.dtd)
 #ifdef MOZ_SAFE_BROWSING
     locale/browser/safebrowsing/phishing-afterload-warning-message.dtd (%chrome/browser/safebrowsing/phishing-afterload-warning-message.dtd)
@@ -103,8 +105,11 @@
 *   locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
 *   locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
 % locale testpilot @AB_CD@ %locale/feedback/
     locale/feedback/main.dtd                       (%feedback/main.dtd)
     locale/feedback/main.properties                (%feedback/main.properties)
+% locale webapprt @AB_CD@ %locale/webapprt/
+    locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
+    locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -43,16 +43,18 @@ browser/Makefile
 browser/app/Makefile
 browser/app/profile/extensions/Makefile
 browser/base/Makefile
 browser/components/Makefile
 browser/components/about/Makefile
 browser/components/build/Makefile
 browser/components/certerror/Makefile
 browser/components/dirprovider/Makefile
+browser/components/downloads/Makefile
+browser/components/downloads/src/Makefile
 browser/components/feeds/Makefile
 browser/components/feeds/public/Makefile
 browser/components/feeds/src/Makefile
 browser/components/migration/Makefile
 browser/components/migration/public/Makefile
 browser/components/migration/src/Makefile
 browser/components/places/Makefile
 browser/components/places/src/Makefile
@@ -126,16 +128,18 @@ else
 fi
 
 if [ "$ENABLE_TESTS" ]; then
   add_makefiles "
     browser/base/content/test/Makefile
     browser/base/content/test/newtab/Makefile
     browser/components/certerror/test/Makefile
     browser/components/dirprovider/tests/Makefile
+    browser/components/downloads/test/Makefile
+    browser/components/downloads/test/browser/Makefile
     browser/components/preferences/tests/Makefile
     browser/components/search/test/Makefile
     browser/components/sessionstore/test/Makefile
     browser/components/shell/test/Makefile
     browser/components/feeds/test/Makefile
     browser/components/feeds/test/chrome/Makefile
     browser/components/migration/tests/Makefile
     browser/components/places/tests/Makefile
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -57,9 +57,13 @@ EXTRA_JS_MODULES = \
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows) 
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
 	WindowsJumpLists.jsm \
 	$(NULL)
 endif
 
+EXTRA_PP_JS_MODULES = \
+	WebappsInstaller.jsm \
+	$(NULL)
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/modules/WebappsIconHelpers.js
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This function receives a list of icon sizes
+ * and URLs and returns the url string for the biggest icon.
+ *
+ * @param aIcons An object where the keys are the icon sizes
+ *               and the values are URL strings. E.g.:
+ *               aIcons = {
+ *                 "16": "http://www.example.org/icon16.png",
+ *                 "32": "http://www.example.org/icon32.png"
+ *               };
+ *
+ * @returns the URL string for the largest specified icon
+ */
+function getBiggestIconURL(aIcons) {
+  let iconSizes = Object.keys(aIcons);
+  if (iconSizes.length == 0) {
+    return "";
+  }
+  iconSizes.sort(function(a, b) a - b);
+  return aIcons[iconSizes.pop()];
+}
+
+/**
+ * This function retrieves the icon for an app as specified
+ * in the iconURI on the shell object.
+ * Upon completion it will call aShell.processIcon()
+ *
+ * @param aShell The shell that specifies the properties
+ *               of the native app. Three properties from this
+ *               shell will be used in this function:
+ *                 - iconURI
+ *                 - useTmpForIcon
+ *                 - processIcon()
+ */
+function getIconForApp(aShell, callback) {
+  let iconURI = aShell.iconURI;
+  let mimeService = Cc["@mozilla.org/mime;1"]
+                      .getService(Ci.nsIMIMEService);
+
+  let mimeType;
+  try {
+    let tIndex = iconURI.path.indexOf(";");
+    if("data" == iconURI.scheme && tIndex != -1) {
+      mimeType = iconURI.path.substring(0, tIndex);
+    } else {
+      mimeType = mimeService.getTypeFromURI(iconURI);
+    }
+  } catch(e) {
+    throw("getIconFromURI - Failed to determine MIME type");
+  }
+
+  try {
+    let listener;
+    if(aShell.useTmpForIcon) {
+      let downloadObserver = {
+        onDownloadComplete: function(downloader, request, cx, aStatus, file) {
+          // pass downloader just to keep reference around
+          onIconDownloaded(aShell, mimeType, aStatus, file, callback, downloader);
+        }
+      };
+
+      tmpIcon = Services.dirsvc.get("TmpD", Ci.nsIFile);
+      tmpIcon.append("tmpicon." + mimeService.getPrimaryExtension(mimeType, ""));
+      tmpIcon.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+
+      listener = Cc["@mozilla.org/network/downloader;1"]
+                   .createInstance(Ci.nsIDownloader);
+      listener.init(downloadObserver, tmpIcon);
+    } else {
+      let pipe = Cc["@mozilla.org/pipe;1"]
+                   .createInstance(Ci.nsIPipe);
+      pipe.init(true, true, 0, 0xffffffff, null);
+
+      listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
+                   .createInstance(Ci.nsISimpleStreamListener);
+      listener.init(pipe.outputStream, {
+          onStartRequest: function() {},
+          onStopRequest: function(aRequest, aContext, aStatusCode) {
+            pipe.outputStream.close();
+            onIconDownloaded(aShell, mimeType, aStatusCode, pipe.inputStream, callback);
+         }
+      });
+    }
+
+    let channel = NetUtil.newChannel(iconURI);
+    let CertUtils = { };
+    Cu.import("resource://gre/modules/CertUtils.jsm", CertUtils);
+    // Pass true to avoid optional redirect-cert-checking behavior.
+    channel.notificationCallbacks = new CertUtils.BadCertHandler(true);
+
+    channel.asyncOpen(listener, null);
+  } catch(e) {
+    throw("getIconFromURI - Failure getting icon (" + e + ")");
+  }
+}
+
+function onIconDownloaded(aShell, aMimeType, aStatusCode, aIcon, aCallback) {
+  if (Components.isSuccessCode(aStatusCode)) {
+    aShell.processIcon(aMimeType, aIcon, aCallback);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/modules/WebappsInstaller.jsm
@@ -0,0 +1,358 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+let EXPORTED_SYMBOLS = ["WebappsInstaller"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource:///modules/Services.jsm");
+Cu.import("resource:///modules/FileUtils.jsm");
+Cu.import("resource:///modules/NetUtil.jsm");
+
+let WebappsInstaller = {
+  /**
+   * Creates a native installation of the web app in the OS
+   *
+   * @param aData the manifest data provided by the web app
+   *
+   * @returns bool true on success, false if an error was thrown
+   */
+  install: function(aData) {
+
+#ifdef XP_MACOSX
+    let shell = new MacNativeApp(aData);
+#else
+    return false;
+#endif
+
+    try {
+      shell.install();
+    } catch (ex) {
+      Cu.reportError("Error installing app: " + ex);
+      return false;
+    }
+
+    return true;
+  }
+}
+
+/**
+ * This function implements the common constructor for both
+ * the Windows and Mac native app shells. It reads and parses
+ * the data from the app manifest and stores it in the NativeApp
+ * object. It's meant to be called as NativeApp.call(this, aData)
+ * from the platform-specific constructor.
+ *
+ * @param aData the data object provided by the web app with
+ *              all the app settings and specifications.
+ *
+ */
+function NativeApp(aData) {
+  let app = this.app = aData.app;
+
+  let origin = Services.io.newURI(app.origin, null, null);
+
+  if (app.manifest.launch_path) {
+    this.launchURI = Services.io.newURI(origin.resolve(app.manifest.launch_path),
+                                        null, null);
+  } else {
+    this.launchURI = origin.clone();
+  }
+
+  let biggestIcon = getBiggestIconURL(app.manifest.icons);
+  try {
+    let iconURI = Services.io.newURI(biggestIcon, null, null);
+    if (iconURI.scheme == "data") {
+      this.iconURI = iconURI;
+    }
+  } catch (ex) {}
+
+  if (!this.iconURI) {
+    try {
+      this.iconURI = Services.io.newURI(origin.resolve(biggestIcon), null, null);
+    }
+    catch (ex) {}
+  }
+
+  this.appName = sanitize(app.manifest.name);
+  this.appNameAsFilename = stripStringForFilename(this.appName);
+
+  if(app.manifest.developer && app.manifest.developer.name) {
+    let devName = app.manifest.developer.name.substr(0, 128);
+    devName = sanitize(devName);
+    if (devName) {
+      this.developerName = devName;
+    }
+  }
+
+  let shortDesc = this.appName;
+  if (app.manifest.description) {
+    let firstLine = app.manifest.description.split("\n")[0];
+    shortDesc = firstLine.length <= 256
+                ? firstLine
+                : firstLine.substr(0, 253) + "...";
+  }
+  this.shortDescription = sanitize(shortDesc);
+
+  this.manifest = app.manifest;
+
+  this.profileFolder = Services.dirsvc.get("ProfD", Ci.nsIFile);
+}
+
+#ifdef XP_MACOSX
+
+function MacNativeApp(aData) {
+  NativeApp.call(this, aData);
+  this._init();
+}
+
+MacNativeApp.prototype = {
+  _init: function() {
+    this.appSupportDir = Services.dirsvc.get("ULibDir", Ci.nsILocalFile);
+    this.appSupportDir.append("Application Support");
+
+    let filenameRE = new RegExp("[<>:\"/\\\\|\\?\\*]", "gi");
+    this.appNameAsFilename = this.appNameAsFilename.replace(filenameRE, "");
+    if (this.appNameAsFilename == "") {
+      this.appNameAsFilename = "Webapp";
+    }
+
+    // The ${ProfileDir} format is as follows:
+    //  host of the app origin + ";" +
+    //  protocol + ";" +
+    //  port (-1 for default port)
+    this.appProfileDir = this.appSupportDir.clone();
+    this.appProfileDir.append(this.launchURI.host + ";" +
+                              this.launchURI.scheme + ";" +
+                              this.launchURI.port);
+
+    this.installDir = Services.dirsvc.get("LocApp", Ci.nsILocalFile);
+    this.installDir.append(this.appNameAsFilename + ".app");
+
+    this.contentsDir = this.installDir.clone();
+    this.contentsDir.append("Contents");
+
+    this.macOSDir = this.contentsDir.clone();
+    this.macOSDir.append("MacOS");
+
+    this.resourcesDir = this.contentsDir.clone();
+    this.resourcesDir.append("Resources");
+
+    this.iconFile = this.resourcesDir.clone();
+    this.iconFile.append("appicon.icns");
+
+    this.processFolder = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+  },
+
+  install: function() {
+    this._removeInstallation(true);
+    try {
+      this._createDirectoryStructure();
+      this._copyPrebuiltFiles();
+      this._createConfigFiles();
+    } catch (ex) {
+      this._removeInstallation(false);
+      throw(ex);
+    }
+
+    getIconForApp(this);
+  },
+
+  _removeInstallation: function(keepProfile) {
+    try {
+      if(this.installDir.exists()) {
+        this.installDir.followLinks = false;
+        this.installDir.remove(true);
+      }
+    } catch(ex) {
+    }
+
+   if (keepProfile)
+     return;
+
+   try {
+      if(this.appProfileDir.exists()) {
+        this.appProfileDir.followLinks = false;
+        this.appProfileDir.remove(true);
+      }
+    } catch(ex) {
+    }
+  },
+
+  _createDirectoryStructure: function() {
+    if (!this.appProfileDir.exists())
+      this.appProfileDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+
+    this.installDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    this.contentsDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    this.macOSDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+    this.resourcesDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+  },
+
+  _copyPrebuiltFiles: function() {
+    let webapprt = this.processFolder.clone();
+    webapprt.append("webapprt-stub");
+    webapprt.copyTo(this.macOSDir, "webapprt");
+  },
+
+  _createConfigFiles: function() {
+    // ${ProfileDir}/config.json
+    let json = {
+      "app": {
+        "profile": this.profileFolder.path,
+        "origin": this.launchURI.prePath,
+        "installOrigin": "apps.mozillalabs.com",
+        "manifest": this.manifest
+       }
+    };
+
+    let configJson = this.appProfileDir.clone();
+    configJson.append("webapp.json");
+    writeToFile(configJson, JSON.stringify(json), function() {});
+
+    // ${InstallDir}/Contents/MacOS/webapp.ini
+    let applicationINI = this.macOSDir.clone().QueryInterface(Ci.nsILocalFile);
+    applicationINI.append("webapp.ini");
+
+    let factory = Cc["@mozilla.org/xpcom/ini-processor-factory;1"]
+                    .getService(Ci.nsIINIParserFactory);
+
+    let writer = factory.createINIParser(applicationINI).QueryInterface(Ci.nsIINIParserWriter);
+    writer.setString("Webapp", "Name", this.appName);
+    writer.setString("Webapp", "Profile", this.appProfileDir.leafName);
+    writer.setString("Branding", "BrandFullName", this.appName);
+    writer.setString("Branding", "BrandShortName", this.appName);
+    writer.writeFile();
+
+    let infoPListContent = '<?xml version="1.0" encoding="UTF-8"?>\n\
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n\
+<plist version="1.0">\n\
+  <dict>\n\
+    <key>CFBundleDevelopmentRegion</key>\n\
+    <string>English</string>\n\
+    <key>CFBundleDisplayName</key>\n\
+    <string>' + escapeXML(this.appName) + '</string>\n\
+    <key>CFBundleExecutable</key>\n\
+    <string>webapprt</string>\n\
+    <key>CFBundleIconFile</key>\n\
+    <string>appicon</string>\n\
+    <key>CFBundleIdentifier</key>\n\
+    <string>' + escapeXML(this.launchURI.prePath) + '</string>\n\
+    <key>CFBundleInfoDictionaryVersion</key>\n\
+    <string>6.0</string>\n\
+    <key>CFBundleName</key>\n\
+    <string>' + escapeXML(this.appName) + '</string>\n\
+    <key>CFBundlePackageType</key>\n\
+    <string>APPL</string>\n\
+    <key>CFBundleSignature</key>\n\
+    <string>MOZB</string>\n\
+    <key>CFBundleVersion</key>\n\
+    <string>0</string>\n\
+#ifdef DEBUG
+    <key>FirefoxBinary</key>\n\
+    <string>org.mozilla.NightlyDebug</string>\n\
+#endif
+  </dict>\n\
+</plist>';
+
+    let infoPListFile = this.contentsDir.clone();
+    infoPListFile.append("Info.plist");
+    writeToFile(infoPListFile, infoPListContent, function() {});
+  },
+
+  /**
+   * This variable specifies if the icon retrieval process should
+   * use a temporary file in the system or a binary stream. This
+   * is accessed by a common function in WebappsIconHelpers.js and
+   * is different for each platform.
+   */
+  useTmpForIcon: true,
+
+  /**
+   * Process the icon from the imageStream as retrieved from
+   * the URL by getIconForApp(). This will bundle the icon to the
+   * app package at Contents/Resources/appicon.icns.
+   *
+   * @param aMimeType     the icon mimetype
+   * @param aImageStream  the stream for the image data
+   * @param aCallback     a callback function to be called
+   *                      after the process finishes
+   */
+  processIcon: function(aMimeType, aIcon) {
+    try {
+      let process = Cc["@mozilla.org/process/util;1"]
+                    .createInstance(Ci.nsIProcess);
+      let sipsFile = Cc["@mozilla.org/file/local;1"]
+                    .createInstance(Ci.nsILocalFile);
+      sipsFile.initWithPath("/usr/bin/sips");
+
+      process.init(sipsFile);
+      process.run(true, ["-s",
+                  "format", "icns",
+                  aIcon.path,
+                  "--out", this.iconFile.path,
+                  "-z", "128", "128"],
+                  9);
+    } catch(e) {
+      throw(e);
+    }
+  }
+
+}
+
+#endif
+
+/* Helper Functions */
+
+/**
+ * Async write a data string into a file
+ *
+ * @param aFile     the nsIFile to write to
+ * @param aData     a string with the data to be written
+ * @param aCallback a callback to be called after the process is finished
+ */
+function writeToFile(aFile, aData, aCallback) {
+  let ostream = FileUtils.openSafeFileOutputStream(aFile);
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                    .createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let istream = converter.convertToInputStream(aData);
+  NetUtil.asyncCopy(istream, ostream, function(x) aCallback(x));
+}
+
+/**
+ * Removes unprintable characters from a string.
+ */
+function sanitize(aStr) {
+  let unprintableRE = new RegExp("[\\x00-\\x1F\\x7F]" ,"gi");
+  return aStr.replace(unprintableRE, "");
+}
+
+/**
+ * Strips all non-word characters from the beginning and end of a string
+ */
+function stripStringForFilename(aPossiblyBadFilenameString) {
+  //strip everything from the front up to the first [0-9a-zA-Z]
+
+  let stripFrontRE = new RegExp("^\\W*","gi");
+  let stripBackRE = new RegExp("\\W*$","gi");
+
+  let stripped = aPossiblyBadFilenameString.replace(stripFrontRE, "");
+  stripped = stripped.replace(stripBackRE, "");
+  return stripped;
+}
+
+function escapeXML(aStr) {
+  return aStr.toString()
+             .replace(/&/g, "&amp;")
+             .replace(/"/g, "&quot;")
+             .replace(/'/g, "&apos;")
+             .replace(/</g, "&lt;")
+             .replace(/>/g, "&gt;");
+}
+
+/* More helpers for handling the app icon */
+#include WebappsIconHelpers.js
--- a/browser/modules/webappsUI.jsm
+++ b/browser/modules/webappsUI.jsm
@@ -6,16 +6,17 @@ let EXPORTED_SYMBOLS = ["webappsUI"];
 
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Webapps.jsm");
+Cu.import("resource://gre/modules/WebappsInstaller.jsm");
 
 let webappsUI = {
   init: function webappsUI_init() {
     Services.obs.addObserver(this, "webapps-ask-install", false);
     Services.obs.addObserver(this, "webapps-launch", false);
   },
   
   uninit: function webappsUI_uninit() {
@@ -103,18 +104,21 @@ let webappsUI = {
 
   doInstall: function(aData, aBrowser, aWindow) {
     let bundle = aWindow.gNavigatorBundle;
 
     let mainAction = {
       label: bundle.getString("webapps.install"),
       accessKey: bundle.getString("webapps.install.accesskey"),
       callback: function(notification) {
-        installDone = true;
-        DOMApplicationRegistry.confirmInstall(aData);
+        if (WebappsInstaller.install(aData)) {
+          DOMApplicationRegistry.confirmInstall(aData);
+        } else {
+          DOMApplicationRegistry.denyInstall(aData);
+        }
       }
     };
 
     let requestingURI = aWindow.makeURI(aData.from);
     let manifest = new DOMApplicationManifest(aData.app.manifest, aData.app.origin);
 
     let host;
     try {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..abff1bc4e26a27dae77a48b861179a1024829ab0
GIT binary patch
literal 2844
zc$@(o3*+>OP)<h;3K|Lk000e1NJLTq001fg001}$1^@s6i`{C=000W(Nkl<Zc-qyO
zdrVYU9><r_fnr51O2t=LaIL#kcQ<XU4@7(-z6C)Pe1Iq_prU{eR8)L}uWEdt8}R|Y
z1t0C&WVh)iX3d(Anyz$f$NI-M8k?}KR$0?=)w7@9?3u|-=H40TA7xAO$vOAjIo$7@
z$M5&OXMUlS`dQ#{F@F5`CZj*!Y_49t>LdVKL!d5OY24caphbmRpe=NRw;&S2p|jL=
zTQ~g~i=oh2vu0&L7<K1$C}cH3Xa>0YK`3OI2yLx}#4TL7@DE3h9Lb}e1c{ZEl{pI*
zEI3MiD0GL`UP9;0nUg(t?p&3Xm351H7=%)<lQ!ykW<n=Tnv@L+Zb2A?LY;2QGY}dC
zZ_S@S|Ja~GgVeZj;~rO6S6@1P`gARyc_eM~=FO|(Grxu)BcWY$a&qbyFJ7z`En0L7
z>ZDGaje9-JgeD~=bxlrAu1`rxQIxl!PS<VR>tP_&3Q>$?{)7n=p2Wn&sEHFNKIKa}
z>SM=_Z9H`7P$8e`309-24?s{vVPRqYs#U9$K;6cuM#()NQy(y8%9M!d)2G*`r>83+
zEiKK4P~Fc<Xe$W6a^=dDs;a6F5ZE|m$PgtU{MpHqC$DjDDxc{Btqj-bvSrIUu359@
z(~TQ9DuMcEw7uzXjn0@cqhm%!#;1HKB~bs2Hr>xyC?uUZbEbCc)TvL04<D`sW}@-b
zsZ&?^OEwkC2!^g*yS8rg=FK9~2K7>>?H2dK%!FoUW;)h#c6PR`EgRHJUDi7H!b~<(
z)bZoTb5m1OAB`S8T9(N(1UE{Z^6}B5M;FUy45xnGx^+1sY}2Mqw*373s3l95L{N8#
z<zBv-P=v`&RbV4RM9rKzGlGp$&$S58H`z?RdB#cBqY{BEfop82YtyDpdm?S@jnk%^
ziGO58PytizSX;u<rAs5Eje4Muskb7iVD{|Uu6_uPpiTEP9MSF<FJ2tQV4SO{s8}g$
z5>nV;MYNrjdnVz@_259y4M3eXKWo&ypLxI!5h1M6zLzdt`jxClfqGAA(;`E_($Xo^
zqDM9etk6nhl{S6P;+{f(7Pyb*Pk^a834{*N6(Xe6*Ma(eiwg~aHd<UPOozF;jFonM
zKfqKdyEcJce1kY>mr|~qC?WxFfL)gfK7<BoG2@9W)wDN|=U+-G*R)U2_k#?DB5oW;
zL}H+w@`f%I?a97GlCQ^s0z2H$rGjJ=p&cNWgmMajiah@+F){IbDY=#ueqJ0FykRJm
zta2Os)j;6x&2<|ox&BVteF^0uw=p3N<ZeelDY^bm+D(LZhv@|PN2Jf^WU5atU%q^X
z@-M@N4f~I{D(<I2cSE6hd3pBS+}w{`agmbi52f9gP%@02T<y`1>kp;fL}*XQ89H?6
zD@@F{)K5b>RAX%ZMf*$Y^Wc|;Ldi_^xTXfuSU0(rvY?=#fs|M8OQ^Uf9@sz{bo7%_
zuBF{Ts1<$(YZ-CL@y~HhH$*Ju#|a4u&zTYP5k*kw>eZ|3S<H&k&N{hXTwE-rLTI{~
zNSsnAo35U9tSIelzU#VF>HHap><kkz%hg!K#t|b%sQCE!mwc(mQjQ!sQk+|@;Tm1J
za;3vPVMNr14IA!JzgbdJq7ZtM5l-VXt&K!t=|b_bb!<Atrn^V|CMZPTM5sqUoozZo
z3?`&pObEghYxx->zn3~~-}2m=+S=MgnRbs|kNVK{>(|$9-@aXymX_L>?kL$15F(|S
zNUU5a0_)gpip^$YWPSG2sWI}{v19W^5K}DfNzAec+l(10gv^?nn&BeSD~=*EXUmo?
z3QK3hF}a+%31$}0Uq26v$F3HUPk5o*k8?_*{rAulx<LmBa%LuDi|1dYvBmS>&&%TR
zZd5=>8miq>polWUx5e{MbNd-tJQ=}`zC^ZEt~a!J2I2yo7zu3&!9matI%(QNh%Q6n
zb+9%|C?O;AM~ZUq=WgvfLNpAABu%`&jw4K_=*`vMODMZP7_x|?m4eiDTO<kKz7InZ
z3|UaA$<l3+;L%Tu>;kc8&z{XA$$UgkkkEFwsHo_Z3l}aNIehqVCHen}2=~fNydL+3
zI|O(|X9v_JWZj|tJ|`y1NaW<nllzhe?|^bgx7`QjIo(V#B6scD_5PSKV;rX=?$Gvc
zGSd%I=X3L+mrE#Of)R6<7-}bQmlAGz3z(4#zNG>==J66r`U~cr{4Ut3mm;dSz;y+N
zMB=2P?k@B-c$<-2f}p3Ij=bWTU*VWOK5^p2-+1-|v3LUq4pf5&4;G=TAlfB#+qP{*
z%a<=#IHYyXaD(_LV(SCR^UhH=a7;yH7!l;^M?H#k_`rF*L)~3y2Z$$zUdJ8D|GYyS
zEhpfwCGcM(Q~eMd8>@u4xHu7-553$o)5R4lR>)bC!};voxigY<dS6810UVGFyo4g;
zB5O-=;^F9rTZ)AH(uR)}M6A2eH(@GS?4NRk!@<J|u4nDuy*nFU`b65CU{pkCipLsd
z<Z8(Gs=U11d0u3Jwh-cu!7ED*rbHpkPd`tgEUT|ajT)seCdar=CC|s4J9jRPj8#m%
zoU(-Fk)fkPJNFSS#*C=Fd-twtPUt~oF)37>lAnH_Q_t-DRnD21#eZ<U9?~^)U<M3>
zULq1&yM)rQy;!?_`}Wz%zsn9DJQ%K#dWqSCndbHgy~kOT9VfL6!UbLT>gU`{_|O75
zxufkB>;E?H=_s3NA3Cn_zr=^W3vU=^COW()2V@5h98jF5IoQ8{zhb?9$@x$>GqHHj
z_ryI(_;R9c-<P2Kd55Pxd-m)X_arXL9im4A_Y&oEW+~LdKkh^Z%4Hc;2~w9fKc>UN
z&hNy?<}w;pg4Ahq^)m|i7?L>FXaRfU&wR13__B8ytUofkbBP4el1lyMdMmu9vHB3$
zjDF@Rl#LS)sT#>dztQ)sBJ%&!lu*}g7DJl{Kea%s-dI8IKYn@A@<M~53%sp~fDkR%
zztEEZ{YC$XhnGNfGlYi0AcFn}WTYL`Gvyk(LmO!2Q>f%}GSmy=X$6Eg^}*OU`QhsY
zWd(#cL1-k*7V`nGh_?@TD?O|O{<{#amqmb~P>Fw-1xH>do<7A*t%kP#9?Uzr#6Abq
zpW>cYgK0ngBf@eK+P{B)HDJI1F(5B+QTIrj<=Q~^aohO;9zqc*cFqN9RbfmXZr!>y
z%g>pU92&U5peWiNV($$5>HNJ;91>z(i2o%2+cD^M+|Pk`paWPugo-$VuLqo#C@i4b
zv17;Q7?x<0G%7C115ApzCK3KQL>mben};9uLhPNKJMr8_&R<46gd$9)+ygNxY%n>i
z{DO{sOpJ+Qoq3>-bVT$Q2<~Gf6dNaY&I@U0={#mO7aIx{QH+{{va&M8$%$;PZy4#N
zOl3Psl}1A4keBr5fb^;KuXf+CtC#YcqWgIZ<(x^T+J#)p;V%RB_w(n^7h?DN8BRTG
zQDFvTnnh$IdAE!QclMV?5h!m4iq{qq+sJ5^G2ObK`DWr(MB(TK`LXr|ru}PN)%yrc
z<$hn&&4f4^P1!`pIUDN9KMiu43V%XHBu3;oQ|-5(4o?}{Ey<UBNO-D5U?ygypH2qB
zU@;6&2rR?M_7hXloTDA4x{USM<bK-a$KaU$w3k0l?iL_0hPEE?y5E4WN5ay~tkmp&
uI{S3t=k9kH#ozi)p)GGp0wK7mrsTiPD0l}?0`CL>0000<MNUMnLSTY<V0(f9
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..507c627f765990037170bbb5244c7a3b686a59cb
GIT binary patch
literal 2102
zc$|Gz3pA8zA0N{c+JtCH(rbtkGw(a@lVNTc)<`bLrC4L;#Vm7a<{ic@my}eFOT?CL
zq?I<?7FI}VEycR;MxoBvx)o+vE7X>6RNC)+=d{l`@B2Le|M~s?&+qv^zjK~UFHcvs
z^?K`3D3qETgYK=E!Ie*Co#OA0Udm8RTM*{}#77X0#Ii&%ioy|u!GIf&6#;w0EKc0P
zhp+<*wZ@q18-N5bJ&0@p564=;;G{euYGpV$NrfzS6pR32a0Hi6!piIKVgW9Pg!Q*(
zf=nR|j^r}pMX*o2r!PA`ifzxqIynLkQlg>&4@Ovklo!nx6Qv~V`?^HMdL@m=0`DP6
z6bbu5Q~=Wppb11UV2uMU*q{vr*jV8pE70D`-W(u+5Qv8ec+l1Y0*ThPL=XZ#4y<A~
z5htAJO?UaYmm(rzBN0SM#N%UQV{x&TIDsev582z><3R$RK(J6CEW~kqgeA4$i;Y$*
z&|xuK#1$f30Uub2Sz!VRLc(HKkH8atD$5stY?Gp4cqvPWhj1YNe}GKpze9PvPiQgX
z4Tt|bTI?Gqgz?_6SRfIx6&Dw7v_cjVX(E_~2t>XDLG)@Dy&?sOKpZI$0yH0M90V{~
zY%YH#^Pa(E65aS>gvDpWZgdh>k->4f93n_1KoHdmqJh@V5abNn(P(ybXQ~TCu(u)5
zD7LFyx_~X=!F*(u%lU^(_$+tD3?JCAh#LcQTtosM@cz(5?q{`Fx_n;C2QKIHTENe8
z@d`2c|GVndl)^tN%TLBt1fQ%A^A&y<DU3ZcJ8~a|!j!wwDZbJtQ-K|VA-eX3ErXLC
zM>%Jw!nvx<%OiQ-^J(sWSD$yL?pEGd&S1P%pUCpr+myAt<cNEL-p+pCeU#0VGQ%v4
zruJpV#ij~pV~cj1H*i8r-<AmdJ01-eo(@;!S&$y@u@t<0bLWS+8faXf)TK=6TFTX6
zs2F25p}SszgBFXmi$SK#hZ7o-2bmLZ|5$jgIf1rY7cqnemHTUbu!n(*Yc?LCYos<M
z&756>O)SCyQgo+RiOS*`p-`4dKcG%-I%*Q1nnV89q;ag^lCL2NIYvm|VZ6imI)~11
zd8neh_iA-``gb#Bw<c*{7;Z3ES4~G<(xFZ!psZfH8Z_?+MWJi*y4$X}q^v)vq*OV2
zuUj?!k-7!`UPR3HxW}!@)+SNQeLA&WS)tH9b8++cBjJ6^S)<oQbnCF6%c-@Q^A@2?
z9#;o;Ui6qRHS&=fnAQ7@XM1Y~YhxU)Z-VBXQgs{yNW-%x{nwNk^r=BsuT4cX!7_F1
z8BsEu()+w6W3IY2+WWw1^5znE-5K=ObmgExr?sjYX_k$5{kO^U@2Yf;-yF}?KP9d(
z+gH$S0I(u&cklDe(XIY^flN*-k~T+V$alAn6l3)&f2vA4j12nc8kl8=IQrapr?qC#
zF$(}%Mv7~;Pwde7=|$OVDtQ4lv?f|L4_eN-sqce{=2==AIe;4IeOq-ml#)Tpm!+DR
z=Az=mQA*NMotbkc5PuU@DbsYh?nN)n)OA}3XkMIjb2_|(ifqh(ckt$~f2=jE;j#&{
zdBeKNkYDBv#|Be*>=u$xOUW*{Ql6NmcD}G0bAozXIQ6Sx*%9`4JxO&jtU?+0PvTRX
znlI!k=-R@8?0PNv!osU^%5ce2*|!p0!|cn!vA=sx-An6uvhe1_==`MV(+!-t|E#N<
zpYzW@-Pb=|y6*Samr{|^2Gzw-opVJs*ZRau`>XOB8>(Wm_>KcAUrtMtWxLb+Mf|v=
z9<3;+bLZQKPuD%Ur6hlqd+M)H$pG4`&qVgyfx5&*!{osqPKE>q)yv+7{Jm?S;XW;-
z9kOq|R(heU_(8I%<T7p`bHC<`uT%~Wzy8Kl(xH8I)Vb8<<WSqQyM~BZG8^3UAaFc<
zfid(f$hCceZ+FEuMlx0~)Hc%oB9F0n=A|czoT8qG>TR;@#_mCT`Jochz4-8?e`lc#
zcRu1|+nMas{f#l^HizQII5OqiE|mpD_rh=XMkac>eV+hKMd<sr{)jq?AuAa#Pd~z!
zi{@lm3zx&%wl7D&@|C%U#_f`nZGA<fEu@6JOS4zXIX<Gz@>{&}7f^3j&@68o$oJC{
ztIcX1mmNusL&ap=82v>Lst}dn$ny1%d(J;_SUd20lTWwj9EGHwZk*_MCvojD*OU$W
zF^nANs-F18#o@*YEGb>1_p4nuB5A+Wc=R48{yZ6UqFc4;Yhr!pn>)GN?3&aPLjxI@
z7HP2Ct)htTKZn!gOVd>@_xlahgFB2gY7OQN>CaF$)x2?kJT`5`e>74pwbk=8Te=m$
zr{io&b8v%a_WU_pT<T8Lbo^rDp`w<D8M1UT6)?=vI3{-39E!@>MY!lWbrikwlDj#3
K(krQ<Y5xTY9A?P?
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bd548b18382388cfbe7672d1d0725bff0d21432c
GIT binary patch
literal 3166
zc$|G#3piA1A3rn17`7%F6_sOLw$$91p&2IQerv|HD2bUd24gNe7bc5lscaTq6cL5m
zl3Gnm)DpH_%C)VntS+|8A}Y5ek-j78>-(OkeV^x?^Zx(u@AtpG|MQ;b?Db)~eS<VW
z0s!!hhr6>M^i<AX2o30dZ3C+n0MtyQ0)k{gjEz)|NPuI7i`ZP8LLdfahn>Ab%;NC5
zGLX&XMG0w`q3Sve7!^*#1d<s9hS-T48RZ@?;rhoj132+~4ka98zX7yUP$2^Xu8ajL
z1TjJ>RYAjiv`dBNvuZpB{0NcpX&8su1u%%=13HN$T#$?-U^xV9B4|y*5lI9JiLwr~
zBoGOBq9vYSgC!EEWE(1h2!0wE$eScQg6ik&`pFk0(J+xRnV5>l$Hm3r;;e8Y2@g-C
zP$+nUCEn5!3n8%59YPsPffY(k<}EmLr5s6=SQaG`g0nJ<Et1P<7|eVM0`X_FLg}Y6
zK?TDrSYkX8N5FppWHA06DiC}|OJ#oCh<`^*19phHct5UGB$sfw5Zq*zET%e1xGb4S
z5+D-A%vaGTQX~^eBSm7+$)AiPf(#ZXN;s?e$Y3z29zv;%CFF2DoM{+HgNup^r&3&;
zoSg||lA|ldib!;UN=|g3thaP=a3Z@}xj0(RbDc#TxqvH_&2z*5%O!u6JKGF%Y_23K
zmK*LW5edMLNmHY~9*gDIW0~WIe?1nfuX6EFF!=x5>UkB^KeN-%jSC5%Tc0b0`dtDw
zHnM|f54{x2WiCN7e^G=ijwRs&bCvfHuz1kku)=qIcY|vO0En9&&JF>J?w19L8xL<<
z(Hw%!IeSNPaT~+tbZh&a(cP*uq*e;a6@&W@lZFRHfjG>ld0N_e<=vpXOEME_M&qr2
zqN!5fKV_r`LAVtgd-*@@KBDw7@UA;wP+g?w)OjVR&#rS~-;ndGYgg*Vrjj~#bWl1e
zeG7kG$4C9Mov5X*^Z<dx$mzEuv3GoY(^vmtp5{yZ%fg$f9#&C;F*R+-vlir$6W_^#
z5f?!7hEr@)0Cuy@Bg<Ugc8qr4)7@RAtEVS0YuMF=GGUtmGn*oVg1UOVve#WG7@0s4
zJ<o;wm<zmhOF7Lf=&q4H_v1IFp<NLuFrtA8kkAGOd4!!a37obGEuu**yztER>(`C4
zvB425fFGA%&;uClxmJDNGSx;kfavDtR<#&lG&VFW<JEeP^|nu?0+ANh)^|NUJuUsT
z_1CEo%Aa8L7wSp?1;FV*1>nwPTKjz#E5wf-*P59%x*NwrA_9Pc0JLUD1<sr~<C-=H
zLMXGJJb7}iqJm_yyG!d6Bog-bh$4N-oPYwb*sT@Uw_cX~e8BNbU*6s}bG|~b+iSW;
z<FB|Pp#<hU-GqZCCfD2-&x$g%k&)<^truIqh+``d9uZ-%=3@Y5;@(FCcg|)kVShX4
z4_>%{zIk)@0nDt}k4$wwzooqCf5|+a;XL_O5|f!ZA6W<>kxDbZh&;6RK}&0^mEVsA
z9}R2MlagxZ4TsiM#=hJN<z1j&|8zk6fL_X)m~v}0(6H)RLgU`1cZN#i;$kg5^NL-&
zcW)_=Xh-H@Fql_uCAmm|?Y2Dm@cH3kF@N;g4P}|blA)!5qoZSKadGjhbyC+NBoevW
zP<z$m{VmI_B7j|_5MWPxf$an{b<Yj5m%?DpFIUa5095#<6DBD(+p2^sx%&x=78@Du
z4AEAu4J@l!4Qy+!Fwct4R;_zDIDYI8IQ?~yhsTYa^`knX-;&<Pw*W;(`wt!Z(|^mB
z+!GJAUc7jb|6)Xe3(K`KR@I5Cn}(%Nn=;5x9=CP_h3LrWXwRj^TQA%^fa<!R5aX4z
zIM^vLFz`~3dGfQq(*_0xNI!r74}khx)zzzxEv}A@0e!lp%F0TdYvcIf#=_|jxlK(?
zc0UyEMs?jzc>b1LQB}o(_wQBOY47i$J$&#WzaTsNjV)N8D(X`GU_dXzTum5x8c4Kf
z+Qr)yE=m#Gu7^54n?7u4tjd_=HBNUu>eYN51%j6pBkWV@3(MFtIzppkIzK-@^tbBj
zz8-#3y^&_?pi|lQD;6f1ue~C*P=|DAB_IOL%nil&b~>es=y%nwlf5e|Do!HQmm(a*
z?VX*S_9ne`JvoyO!6?kVZ}brdzjsna-8Q!XMsttn<Y<naFbYrd2~>wALz$gSPfs7~
z*Fz=OAKCYmr;S7=Whc13KR)RYs=Mbx-$njCT}vw~LoJ7}ww%n+U!GBJRr295m^p@E
z3`2-JEi5dGNxRZcw%KDRGgR-3eRSLi#tw*SUhVW$*0b>t&gKPEV7>YRkZx7EV=Q98
zcvqU*m|2{?=Z0$;!TJjk1cw7hj|T5+)LM<oczN7sY$U@ZxH4vP`{?>>U5T*%GwgF|
z#I^c|b(3d~)(P%c4w>#8T2@DNre&vJ?LuQ=LQO|&xlvJa{WR1-A~T-IHkyY5>zC<c
zVaEOUr*GZ5rFZab)r!kx9ZrxAk8sol@ygn$^-}E1ZuvF|>CPYDs}b`ZI`vUuCFSzL
z&4JozSZ~hZ!-*!BbDyNO7xn*DoH`hbNa!7UbY+Tv<Hpgl?v?4Qgd)VrdxI85ZG^g-
zn(4JSTuRnfrK-VSD~OLDn<IS2F5F}H)F!%_YU+g~ZUEwI`lkyuUuYl#%6rOpewexD
z_eRSOd{Re0{r3h0g0TG8#Hm;}Px2H^bK#1TgO>ZAY8l6A8V-ab!fHuVG&C9=>b@nk
zZ!s~Eu4FEzy15_mZws85KIW9VFcHU1$l$5d*7xtdWbf&9GbKB$Idi3}7kR4c@FZu4
zd%y>{QR*q(39l?;pZ1xTEiEmb$%5LE)4Ph!*eQm?%s@JZL9Qz;9q?b%x@?u+MC1Ym
zn(=L(PZsDiF&&(`$f#_;WzAJ7d>l#CXRMCa16Sj4l@|lr>*UX_A5hbsid?ni`SIj*
z2Z1B|r4Ecerm(BZTt$1gx52JOV~11&C?%ZmJ^KRrL+RwsmG((B+t@3kvhpdKz@|dq
zk`N_uBIV#g-QerInG2leH=gMxF^BLxSRs<Edr?Qr?TLV$G_hCj+YOajlbH+X=ood~
z%!Wm#>I27m5}{LOpXsT`)@@l<FrVFq+e1P^G}JWUaGmjI<sytmeiQfh3d&MoQ+>`7
zb(=)gq3mppfGg26?Rj~5ix#J3j6ID35>WMs%PVEYn>BRdq3zAQHDjrN+1Yex($TW6
z=eJLLH5_?EcevR)9h;E-usse9l&bK5-n}banj(B_nEa^sI8|P{>^Rq*`9Mu+F+3)h
z%Yz|{H3hbD(ON@Ad*W??BWrrAb%9O&MdRb+F6=IO2x7RZw6_Nc0@_sDE-eB!4fk~%
zqgDIH4+y$q_L@(#+M9cz-K%Mq0}E6-Z7H@;W;-jPZxcwPy|>KtZsv5)<n7~+*la&P
zF?w0rhlCv}qKNT8?FoS`m3mNHTict(Sk=+}yTO&Ihx)^|HGB9f#Deda-S6)96TANU
z`#*6uKy8YJv(?YbKhUG)E1QP9iqg*qj3ibsO=`L;hu~uD8^XJVA?82_z$)pd`EF(~
z+P9zE@Ti>E>5H(3j>w^F{DHipg=?%}{roqmv-?~`s~Hu82{TGKof5hc4hz$LaBe58
XW9$$UKIlc7{VnitVLF#Nh9&<8<U0OL
new file mode 100644
--- /dev/null
+++ b/browser/themes/gnomestripe/downloads/downloads.css
@@ -0,0 +1,279 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*** Panel and outer controls ***/
+
+#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
+
+#downloadsListBox {
+  width: 60ch;
+  background: transparent;
+  padding: 4px;
+  color: inherit;
+}
+
+#downloadsPanel:not([hasdownloads]) > #downloadsListBox {
+  display: none;
+}
+
+#downloadsHistory {
+  background: inherit;
+  color: -moz-nativehyperlinktext;
+  cursor: pointer;
+}
+
+#downloadsPanel[hasdownloads] > #downloadsHistory {
+  border-top: 1px solid ThreeDShadow;
+  background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
+}
+
+#downloadsHistory > .button-box {
+  margin: 1em;
+}
+
+/*** List items ***/
+
+richlistitem[type="download"] {
+  height: 6em;
+  margin: 0;
+  border-top: 1px solid hsla(0,0%,100%,.2);
+  border-bottom: 1px solid hsla(0,0%,0%,.15);
+  background: transparent;
+  padding: 0;
+  color: inherit;
+}
+
+richlistitem[type="download"]:first-child {
+  border-top: 1px solid transparent;
+}
+
+richlistitem[type="download"]:last-child {
+  border-bottom: 1px solid transparent;
+}
+
+#downloadsListBox:-moz-focusring > richlistitem[type="download"][selected] {
+  outline: 1px #999 dotted;
+  outline-offset: -1px;
+  -moz-outline-radius: 3px;
+}
+
+.downloadInfo {
+  padding: 8px;
+  -moz-padding-end: 0;
+}
+
+.downloadTypeIcon {
+  -moz-margin-end: 8px;
+  /* Prevent flickering when changing states. */
+  min-height: 32px;
+  min-width: 32px;
+}
+
+.blockedIcon {
+  list-style-image: url("chrome://global/skin/icons/Error.png");
+}
+
+.downloadTarget {
+  margin-bottom: 7px;
+  cursor: inherit;
+}
+
+.downloadDetails {
+  margin-top: 1px;
+  opacity: 0.6;
+  font-size: 90%;
+  cursor: inherit;
+}
+
+.downloadButton {
+  -moz-appearance: none;
+  min-width: 0;
+  min-height: 0;
+  margin: 6px;
+  border: none;
+  background: transparent;
+  padding: 5px;
+  list-style-image: url("chrome://browser/skin/downloads/buttons.png");
+}
+
+.downloadButton > .button-box {
+  padding: 0;
+}
+
+/*** Highlighted list items ***/
+
+richlistitem[type="download"][state="1"] > .downloadInfo {
+  border-top: 1px solid transparent;
+  border-bottom: 1px solid transparent;
+  -moz-padding-end: 8px;
+}
+
+richlistitem[type="download"][state="1"] > .downloadInfo:hover {
+  border-radius: 3px;
+  border-top: 1px solid hsla(0,0%,100%,.3);
+  border-bottom: 1px solid hsla(0,0%,0%,.2);
+  background-color: Highlight;
+  background-image: -moz-linear-gradient(hsla(0,0%,100%,.1), hsla(0,0%,100%,0));
+  color: HighlightText;
+  cursor: pointer;
+}
+
+/*** Button icons ***/
+
+.downloadButton.downloadCancel {
+  -moz-image-region: rect(0px, 14px, 14px, 0px);
+}
+.downloadButton.downloadCancel:hover {
+  -moz-image-region: rect(0px, 28px, 14px, 14px);
+}
+.downloadButton.downloadCancel:active {
+  -moz-image-region: rect(0px, 42px, 14px, 28px);
+}
+
+.downloadButton.downloadShow {
+  -moz-image-region: rect(14px, 14px, 28px, 0px);
+}
+.downloadButton.downloadShow:hover {
+  -moz-image-region: rect(14px, 28px, 28px, 14px);
+}
+.downloadButton.downloadShow:active {
+  -moz-image-region: rect(14px, 42px, 28px, 28px);
+}
+
+.downloadButton.downloadRetry {
+  -moz-image-region: rect(28px, 14px, 42px, 0px);
+}
+.downloadButton.downloadRetry:hover {
+  -moz-image-region: rect(28px, 28px, 42px, 14px);
+}
+.downloadButton.downloadRetry:active {
+  -moz-image-region: rect(28px, 42px, 42px, 28px);
+}
+
+/*** Status and progress indicator ***/
+
+#downloads-indicator {
+  width: 35px;
+}
+
+#downloads-indicator-anchor {
+  min-width: 18px;
+  min-height: 18px;
+  /* Makes the outermost stack element positioned, so that its contents are
+     rendered over the main browser window in the Z order.  This is required by
+     the animated event notification. */
+  position: relative;
+}
+
+/*** Main indicator icon ***/
+
+#downloads-indicator-icon {
+  background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
+                              0, 16, 16, 0) center no-repeat;
+}
+
+#downloads-indicator-icon:-moz-lwtheme-brighttext {
+  background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"),
+                              0, 16, 16, 0) center no-repeat;
+}
+
+#downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon {
+  background: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
+                              16, 32, 32, 16) center no-repeat;
+}
+
+#downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
+  background: -moz-image-rect(url("chrome://browser/skin/Toolbar-small.png"),
+                              0, 16, 16, 0) center no-repeat;
+  background-size: 12px;
+}
+
+#downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter {
+  background-image: -moz-image-rect(url("chrome://browser/skin/downloads/download-glow.png"),
+                                    16, 32, 32, 16);
+}
+
+/*** Event notification ***/
+
+#downloads-indicator-notification {
+  opacity: 0;
+  background: url("chrome://browser/skin/downloads/download-notification.png")
+              center no-repeat;
+  background-size: 16px;
+}
+
+@-moz-keyframes downloadsIndicatorNotificationRight {
+  from { opacity: 0; -moz-transform: translate(-128px, 128px) scale(8); }
+  20%  { opacity: .85; -moz-animation-timing-function: ease-out; }
+  to   { opacity: 0; -moz-transform: translate(0) scale(1); }
+}
+
+@-moz-keyframes downloadsIndicatorNotificationLeft {
+  from { opacity: 0; -moz-transform: translate(128px, 128px) scale(8); }
+  20%  { opacity: .85; -moz-animation-timing-function: ease-out; }
+  to   { opacity: 0; -moz-transform: translate(0) scale(1); }
+}
+
+#downloads-indicator[notification] > #downloads-indicator-anchor > #downloads-indicator-notification {
+  -moz-animation-name: downloadsIndicatorNotificationRight;
+  -moz-animation-duration: 1s;
+}
+
+#downloads-indicator[notification]:-moz-locale-dir(rtl) > #downloads-indicator-anchor > #downloads-indicator-notification {
+  -moz-animation-name: downloadsIndicatorNotificationLeft;
+}
+
+/*** Progress bar and text ***/
+
+#downloads-indicator-counter {
+  height: 12px;
+  margin: 0;
+  color: hsl(0,0%,30%);
+  text-shadow: 0 1px 0 hsla(0,0%,100%,.5);
+  font-size: 10px;
+  line-height: 10px;
+  text-align: center;
+}
+
+#downloads-indicator-progress {
+  width: 24px;
+  height: 4px;
+  min-width: 0;
+  min-height: 0;
+  margin-top: 1px;
+  margin-bottom: 2px;
+  border-radius: 2px;
+  box-shadow: 0 1px 0 hsla(0,0%,100%,.4);
+}
+
+#downloads-indicator-progress > .progress-bar {
+  -moz-appearance: none;
+  min-width: 0;
+  min-height: 0;
+  background-image: -moz-linear-gradient(#41a0ff, #2090ff);
+  border: 1px solid;
+  border-color: hsla(0,0%,0%,.6) hsla(0,0%,0%,.2) hsla(0,0%,0%,.2);
+  border-radius: 2px 0 0 2px;
+}
+
+#downloads-indicator-progress > .progress-remainder {
+  -moz-appearance: none;
+  min-width: 0;
+  min-height: 0;
+  background-image: -moz-linear-gradient(#9a9a9a, #a1a1a1);
+  border: 1px solid;
+  border-color: hsla(0,0%,0%,.4) hsla(0,0%,0%,.3) hsla(0,0%,0%,.2);
+  -moz-border-start: none;
+  border-radius: 0 2px 2px 0;
+}
+
+#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-bar {
+  background-image: -moz-linear-gradient(#a0a000, #909000);
+}
+
+#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-remainder {
+  background-image: -moz-linear-gradient(#9a9a00, #a1a100);
+}
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -30,16 +30,20 @@ browser.jar:
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/searchbar.css                  (searchbar.css)
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/urlbar-arrow.png
+  skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
+  skin/classic/browser/downloads/download-glow.png    (downloads/download-glow.png)
+  skin/classic/browser/downloads/download-notification.png (downloads/download-notification.png)
+  skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/videoFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/videoFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/audioFeedIcon.png        (feeds/feedIcon.png)
   skin/classic/browser/feeds/audioFeedIcon16.png      (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0fdb1b4c138eee18daf481dc503d24fb631bef57
GIT binary patch
literal 1673
zc$@)=26p+0P)<h;3K|Lk000e1NJLTq001fg001}$1^@s6i`{C=000J1Nkl<Zc-rll
zX>3$g6vu~YJMDD345c$56og8e0;Vb;7!g6BP%IcD`oTm)z=9}I3fRh46l75-ix>f^
zxR4S+i$Fxch)WO!H>8LlHHc_P#KeFkF5mFq<Y)5oc=z2IzI06d@XMWh=A6EN?{dzW
z7r1=+@_&N1e0Vzug7+N%wiKBz!Xj5hL>5G_h4E=;5;{WU|2rpWWC9N#FJg{)ftYCU
z4T>=SGeuv~1#T3zB92ecOXw)ERjd^Gh^)s}=_7KIxFo(1F+?85Hq{_BSIolags5Oa
zj$@mTkEhT)F;A=&&x$2{eo91rgcge3;z#khI3&)BcSYPFG$L*i9}ss!94GF8=r3}-
zg$ll$A(o3}qK1DUV4*-XiBqsk6#AUeEYXEk-0O&YPmC5t_!!S<gzc(>$Jhd!(Dq`B
zI1Jm85W3qD_l~IW6^h7N2%Idc_&moVwm=-@jBXMeIRc-Gm}%>|I7f+V5?hID7XAFT
zez2G;CgGGVCSn`vBeY6<B3?#xRIEq*qdu4EN#Zthk;}y<;_8f-3HpkNW1fvIU||d*
z0}#dLm!9HcJS$rSW!U08JD5rdKZnm*e9lEA3BT=2XwY0jvEl!JQ*sq6o5A*HGiEqf
zH5EjrBQ7SoiDJf>i12+o0zBy_R)`&p{jl2@MSA&*ag>j55RuoAx0}V^qM3PT#0E`&
z;AZiLXdu@$Fdt+Qd8hb8{4CyT9hr|a_=i}zDfpce@3)T3r}KTt^uT4*`lur()?@b*
zdYCHHNdy?b5{C@_VYc}g73wZAknxyk=KHYefosGRm+fKfenP(!8(g+^*i8*kPnwbK
z;udiOV&BAGEFz`{&J>Fsm8O9C6BeOOtY~NE8mZ&n=QApLi@mJqIK<w~e2?FXM#MC+
zP`oH+(UUF}WtPTdJ6nB0eAqfNFZU3c#XmA!?8EPOain!*{*dPxMdWN6<*7I>X6&6n
z_w=RM!ghbnc)LeR3ef`IDw>JgDK-<=C?4><^%!c!9L{47cJLMXF&F7o=4O9dJkH_~
z;<mA}eMC=N9=#2bRf&p(Dps?>DAL^G9c1+e(L_fb4NAyanJg_HB;VqJy~Sf?W1QKb
zDK1r7yiB7=NSPMT+XpmHrOqi6G8}|heQtf?bYx7H9$se^VR@X_BIv|C#+}I#8Ky>I
zq^O3GB813Se8hzLA-EAHiZCM27!rP==tJJ#1q;Zpl{7V-yeiYR;sp}#YtSg3r#fvg
zh`e5WD=v!ja1q-N;;d`CGg+~*eD97sBhC?5EOOl|xr)YPw1~oe*j6%nt>iHph(|<6
zs6)hQz6(H_$mgGH5#NjF#29M)dRJVj$a6k$tXS=;%Q4<REoH3t5ITcDSmm&Fr!0OW
zfbTd)qMo=#qKp!K557)H+$%bn7h}21V+da+mcVikp&Jm@!6C9mP{RR#&SqE9gnbxC
zZXjFkjw=$`?)RGu1VR&LHLq?8Eq-pHn}{36yaQ#lgkw>PJ<omXhw$pAB1xeW#7APL
z#m~L<bBSBS&F{v__F#;&Xc_K2n!#mQNGFpABSfWTg<cmY>0>*>G{oQsTqeYA<D9PI
znL`~%ti1@CN@$!*u>j}g2%lkjky|VpIGdNCg$|u=|H|}