Merge MC -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Sat, 03 Dec 2011 10:34:26 -0800
changeset 81351 13afcd4c097cf52b3fb653d9c59ee07bd78d863e
parent 81350 3dca8a1ee5febc58c7297986b6f1d219fc5a3321 (current diff)
parent 81233 a68c96c1d8e0a64b9008d95ac52e381d88751521 (diff)
child 81355 c2102c45c8da7870239f2b313359a6da18703a4a
push id21565
push userbhackett@mozilla.com
push dateSat, 03 Dec 2011 20:25:52 +0000
treeherdermozilla-central@13afcd4c097c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge MC -> JM
dom/workers/Worker.cpp
dom/workers/WorkerScope.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit-test/tests/basic/bug685321.js
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/methodjit/Compiler.cpp
js/src/methodjit/LoopState.cpp
js/src/shell/js.cpp
js/src/vm/Stack.cpp
js/src/vm/StackSpace.h
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/XrayWrapper.cpp
security/manager/ssl/src/nsSSLThread.cpp
security/manager/ssl/src/nsSSLThread.h
xpinstall/Makefile.in
xpinstall/public/Makefile.in
xpinstall/public/nsIDOMInstallTriggerGlobal.h
xpinstall/public/nsIXPIDialogService.idl
xpinstall/public/nsIXPIInstallInfo.idl
xpinstall/public/nsIXPIProgressDialog.idl
xpinstall/public/nsIXPInstallManager.idl
xpinstall/public/nsPICertNotification.idl
xpinstall/public/nsSoftwareUpdateIIDs.h
xpinstall/public/xpinstall.js
xpinstall/src/CertReader.cpp
xpinstall/src/CertReader.h
xpinstall/src/Makefile.in
xpinstall/src/nsInstall.h
xpinstall/src/nsInstallTrigger.cpp
xpinstall/src/nsInstallTrigger.h
xpinstall/src/nsJSInstallTriggerGlobal.cpp
xpinstall/src/nsSoftwareUpdate.cpp
xpinstall/src/nsXPIInstallInfo.cpp
xpinstall/src/nsXPIInstallInfo.h
xpinstall/src/nsXPITriggerInfo.cpp
xpinstall/src/nsXPITriggerInfo.h
xpinstall/src/nsXPInstallManager.cpp
xpinstall/src/nsXPInstallManager.h
xpinstall/test/pre_checkin.xpi
xpinstall/test/pre_checkin_trigger.html
xpinstall/test/testXPIDialogService.js
xpinstall/tests/Makefile.in
xpinstall/tests/authRedirect.sjs
xpinstall/tests/browser_auth.js
xpinstall/tests/browser_auth2.js
xpinstall/tests/browser_auth3.js
xpinstall/tests/browser_badhash.js
xpinstall/tests/browser_badhashtype.js
xpinstall/tests/browser_bug540558.js
xpinstall/tests/browser_cancel.js
xpinstall/tests/browser_chrome.js
xpinstall/tests/browser_cookies.js
xpinstall/tests/browser_cookies2.js
xpinstall/tests/browser_cookies3.js
xpinstall/tests/browser_cookies4.js
xpinstall/tests/browser_corrupt.js
xpinstall/tests/browser_empty.js
xpinstall/tests/browser_enabled.js
xpinstall/tests/browser_enabled2.js
xpinstall/tests/browser_enabled3.js
xpinstall/tests/browser_hash.js
xpinstall/tests/browser_installchrome.js
xpinstall/tests/browser_localfile.js
xpinstall/tests/browser_localfile2.js
xpinstall/tests/browser_navigateaway.js
xpinstall/tests/browser_navigateaway2.js
xpinstall/tests/browser_offline.js
xpinstall/tests/browser_opendialog.js
xpinstall/tests/browser_signed_multiple.js
xpinstall/tests/browser_signed_naming.js
xpinstall/tests/browser_signed_tampered.js
xpinstall/tests/browser_signed_trigger.js
xpinstall/tests/browser_signed_untrusted.js
xpinstall/tests/browser_signed_url.js
xpinstall/tests/browser_softwareupdate.js
xpinstall/tests/browser_unsigned_trigger.js
xpinstall/tests/browser_unsigned_url.js
xpinstall/tests/browser_whitelist.js
xpinstall/tests/browser_whitelist2.js
xpinstall/tests/browser_whitelist3.js
xpinstall/tests/browser_whitelist4.js
xpinstall/tests/browser_whitelist5.js
xpinstall/tests/browser_whitelist6.js
xpinstall/tests/bug540558.html
xpinstall/tests/cookieRedirect.sjs
xpinstall/tests/corrupt.xpi
xpinstall/tests/empty.xpi
xpinstall/tests/enabled.html
xpinstall/tests/harness.js
xpinstall/tests/installchrome.html
xpinstall/tests/installtrigger.html
xpinstall/tests/signed-no-cn.xpi
xpinstall/tests/signed-no-o.xpi
xpinstall/tests/signed-tampered.xpi
xpinstall/tests/signed-untrusted.xpi
xpinstall/tests/signed.xpi
xpinstall/tests/signed2.xpi
xpinstall/tests/startsoftwareupdate.html
xpinstall/tests/unsigned.xpi
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -391,30 +391,31 @@ nsRootAccessible::ProcessDOMEvent(nsIDOM
   nsINode* targetNode = accessible->GetNode();
 
 #ifdef MOZ_XUL
   nsRefPtr<nsXULTreeAccessible> treeAcc;
   if (targetNode->IsElement() &&
       targetNode->AsElement()->NodeInfo()->Equals(nsGkAtoms::tree,
                                                   kNameSpaceID_XUL)) {
     treeAcc = do_QueryObject(accessible);
-
-    if (eventType.EqualsLiteral("TreeViewChanged")) {
-      treeAcc->TreeViewChanged();
-      return;
-    }
+    if (treeAcc) {
+      if (eventType.EqualsLiteral("TreeViewChanged")) {
+        treeAcc->TreeViewChanged();
+        return;
+      }
 
-    if (eventType.EqualsLiteral("TreeRowCountChanged")) {
-      HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
-      return;
-    }
+      if (eventType.EqualsLiteral("TreeRowCountChanged")) {
+        HandleTreeRowCountChangedEvent(aDOMEvent, treeAcc);
+        return;
+      }
 
-    if (eventType.EqualsLiteral("TreeInvalidated")) {
-      HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
-      return;
+      if (eventType.EqualsLiteral("TreeInvalidated")) {
+        HandleTreeInvalidatedEvent(aDOMEvent, treeAcc);
+        return;
+      }
     }
   }
 #endif
 
   if (eventType.EqualsLiteral("RadioStateChange")) {
     PRUint64 state = accessible->State();
 
     // radiogroup in prefWindow is exposed as a list,
--- a/accessible/tests/mochitest/treeupdate/test_textleaf.html
+++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html
@@ -91,33 +91,33 @@
 
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, this.containerNode)
       ];
 
       this.invoke = function removeTextData_invoke()
       {
         var tree = {
-          role: ROLE_SECTION,
+          role: ROLE_PARAGRAPH,
           children: [
             {
               role: ROLE_TEXT_LEAF,
               name: "text"
             }
           ]
         };
         testAccessibleTree(this.containerNode, tree);
 
         this.textNode.data = "";
       }
 
       this.finalCheck = function removeTextData_finalCheck()
       {
         var tree = {
-          role: ROLE_SECTION,
+          role: ROLE_PARAGRAPH,
           children: []
         };
         testAccessibleTree(this.containerNode, tree);
       }
 
       this.getID = function removeTextData_finalCheck()
       {
         return "remove text data of text node inside '" + aID + "'.";
@@ -142,17 +142,18 @@
       // remove onclick attribute, text leaf shouldn't have any action
       gQueue.push(new removeOnClickAttr("div"));
 
       // set onclick attribute making span accessible, it's inserted into tree
       // and adopts text leaf accessible, text leaf should have an action
       gQueue.push(new setOnClickNRoleAttrs("span"));
 
       // text data removal of text node should remove its text accessible
-      gQueue.push(new removeTextData("container2"));
+      gQueue.push(new removeTextData("p"));
+      gQueue.push(new removeTextData("pre"));
 
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
@@ -163,24 +164,30 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465">
     Mozilla Bug 545465
   </a>
   <a target="_blank"
      title="Make sure accessible tree is correct when rendered text is changed"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652">
     Mozilla Bug 625652
   </a>
+  <a target="_blank"
+     title="Remove text accesible getting no text inside a preformatted area"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=706335">
+    Mozilla Bug 706335
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="container">
     <div id="div">div</div>
     <span id="span">span</span>
   </div>
 
-  <div id="container2">text</div>
+  <p id="p">text</p>
+  <pre id="pre">text</pre>
 
   <div id="eventdump"></div>
 </body>
 </html>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -198,17 +198,17 @@ pref("app.update.incompatible.mode", 0);
 
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
 //  extensions.{GUID}.update.enabled
 //  extensions.{GUID}.update.url
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
-pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%");
+pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.interval", 86400);  // Check for updates to Extensions and 
                                             // Themes every day
 // Non-symmetric (not shared by extensions) extension-specific [update] preferences
 pref("extensions.getMoreThemesURL", "https://addons.mozilla.org/%LOCALE%/firefox/getpersonas");
 pref("extensions.dss.enabled", false);          // Dynamic Skin Switching                                               
 pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                 // restart.
 
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -203,16 +203,17 @@ let TabView = {
 
     // ___ find the deck
     this._deck = document.getElementById("tab-view-deck");
 
     // ___ create the frame
     this._iframe = document.createElement("iframe");
     this._iframe.id = "tab-view";
     this._iframe.setAttribute("transparent", "true");
+    this._iframe.setAttribute("tooltip", "tab-view-tooltip");
     this._iframe.flex = 1;
 
     let self = this;
 
     window.addEventListener("tabviewframeinitialized", function onInit() {
       window.removeEventListener("tabviewframeinitialized", onInit, false);
 
       self._isFrameLoading = false;
@@ -230,16 +231,22 @@ let TabView = {
         self._tabCloseEventListener = null;
       }
       self._initFrameCallbacks.forEach(function (cb) cb());
       self._initFrameCallbacks = [];
     }, false);
 
     this._iframe.setAttribute("src", "chrome://browser/content/tabview.html");
     this._deck.appendChild(this._iframe);
+
+    // ___ create tooltip
+    let tooltip = document.createElement("tooltip");
+    tooltip.id = "tab-view-tooltip";
+    tooltip.setAttribute("onpopupshowing", "return TabView.fillInTooltip(document.tooltipNode);");
+    document.getElementById("mainPopupSet").appendChild(tooltip);
   },
 
   // ----------
   getContentWindow: function TabView_getContentWindow() {
     return this._window;
   },
 
   // ----------
@@ -348,18 +355,20 @@ let TabView = {
         event.preventDefault();
 
         self._initFrame(function() {
           let groupItems = self._window.GroupItems;
           let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
           if (!tabItem)
             return;
 
-          // Switch to the new tab
-          window.gBrowser.selectedTab = tabItem.tab;
+          if (gBrowser.selectedTab.pinned)
+            groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
+          else
+            gBrowser.selectedTab = tabItem.tab;
         });
       }
     }, true);
   },
 
   // ----------
   // Prepares the tab view for undo close tab.
   prepareUndoCloseTab: function TabView_prepareUndoCloseTab(blankTabToRemove) {
@@ -435,10 +444,34 @@ let TabView = {
 
     // enable session restore if necessary
     if (Services.prefs.getIntPref(this.PREF_STARTUP_PAGE) != 3) {
       Services.prefs.setIntPref(this.PREF_STARTUP_PAGE, 3);
 
       // show banner
       this._window.UI.notifySessionRestoreEnabled();
     }
+  },
+
+  // ----------
+  // Function: fillInTooltip
+  // Fills in the tooltip text.
+  fillInTooltip: function fillInTooltip(tipElement) {
+    let retVal = false;
+    let titleText = null;
+    let direction = tipElement.ownerDocument.dir;
+
+    while (!titleText && tipElement) {
+      if (tipElement.nodeType == Node.ELEMENT_NODE)
+        titleText = tipElement.getAttribute("title");
+      tipElement = tipElement.parentNode;
+    }
+    let tipNode = document.getElementById("tab-view-tooltip");
+    tipNode.style.direction = direction;
+
+    if (titleText) {
+      tipNode.setAttribute("label", titleText);
+      retVal = true;
+    }
+
+    return retVal;
   }
 };
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -987,17 +987,17 @@ function makePreview(row)
     newImage.controls = true;
     isAudio = true;
 
     document.getElementById("theimagecontainer").collapsed = false;
     document.getElementById("brokenimagecontainer").collapsed = true;
   }
 #endif
   else {
-    // fallback image for protocols not allowed (e.g., data: or javascript:)
+    // fallback image for protocols not allowed (e.g., javascript:)
     // or elements not [yet] handled (e.g., object, embed).
     document.getElementById("brokenimagecontainer").collapsed = false;
     document.getElementById("theimagecontainer").collapsed = true;
   }
 
   var imageSize = "";
   if (url && !isAudio) {
     if (width != physWidth || height != physHeight) {
@@ -1223,13 +1223,11 @@ function selectImage()
       return;
     }
   }
 }
 
 function checkProtocol(img)
 {
   var url = img[COL_IMAGE_ADDRESS];
-  if (/^data:/.test(url) && /^image\//.test(img[COL_IMAGE_NODE].type))
-    return true;
-  const regex = /^(https?|ftp|file|about|chrome|resource):/;
-  return regex.test(url);
+  return /^data:image\//i.test(url) ||
+    /^(https?|ftp|file|about|chrome|resource):/.test(url);
 }
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -43,18 +43,16 @@ relativesrcdir  = browser/base/content/t
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_feed_discovery.html \
 		feed_discovery.html \
 		test_bug395533.html \
 		bug395533-data.txt \
-		test_contextmenu.html \
-		subtst_contextmenu.html \
 		ctxmenu-image.png \
 		video.ogg \
 		test_offlineNotification.html \
 		offlineChild.html \
 		offlineChild.cacheManifest \
 		offlineChild.cacheManifest^headers^ \
 		offlineChild2.html \
 		offlineChild2.cacheManifest \
@@ -67,16 +65,24 @@ include $(topsrcdir)/config/rules.mk
 		bug364677-data.xml^headers^ \
 		test_offline_gzip.html \
 		gZipOfflineChild.html \
 		gZipOfflineChild.html^headers^ \
 		gZipOfflineChild.cacheManifest \
 		gZipOfflineChild.cacheManifest^headers^ \
 		$(NULL)
 
+# test_contextmenu.html is disabled on Linux due to bug 513558
+ifneq (gtk2,$(MOZ_WIDGET_TOOLKIT))
+_TEST_FILES += \
+		test_contextmenu.html \
+		subtst_contextmenu.html \
+		$(NULL)
+endif
+
 # The following tests are disabled because they are unreliable:
 #   browser_bug423833.js is bug 428712
 #   browser_sanitize-download-history.js is bug 432425
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (santize.xul), if it ever is (bug
 # 480169)
 
--- a/browser/base/content/test/browser_customize.js
+++ b/browser/base/content/test/browser_customize.js
@@ -1,11 +1,12 @@
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   var frame = document.getElementById("customizeToolbarSheetIFrame");
   frame.addEventListener("load", testCustomizeFrameLoadedPre, true);
 
   document.getElementById("cmd_CustomizeToolbars").doCommand();
 }
 
 function testCustomizeFrameLoadedPre(){
   // This load listener can be called before
--- a/browser/components/build/nsBrowserCompsCID.h
+++ b/browser/components/build/nsBrowserCompsCID.h
@@ -46,19 +46,16 @@
 #ifdef XP_MACOSX
 #define NS_SAFARIPROFILEMIGRATOR_CID \
 { 0x29e3b139, 0xad19, 0x44f3, { 0xb2, 0xc2, 0xe9, 0xf1, 0x3b, 0xa2, 0xbb, 0xc6 } }
 #endif
 
 #define NS_OPERAPROFILEMIGRATOR_CID \
 { 0xf34ff792, 0x722e, 0x4490, { 0xb1, 0x95, 0x47, 0xd2, 0x42, 0xed, 0xca, 0x1c } }
 
-#define NS_SEAMONKEYPROFILEMIGRATOR_CID \
-{ 0x9a28ffa7, 0xe6ef, 0x4b52, { 0xa1, 0x27, 0x6a, 0xd9, 0x51, 0xde, 0x8e, 0x9b } }
-
 #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/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -181,18 +181,18 @@ var MigrationWizard = {
       if (this._autoMigrate)
         this._wiz.currentPage.next = "homePageImport";
       else if (this._bookmarks)
         this._wiz.currentPage.next = "migrating"
       else
         this._wiz.currentPage.next = "importItems";
 
       var sourceProfiles = this._migrator.sourceProfiles;
-      if (sourceProfiles && sourceProfiles.Count() == 1) {
-        var profileName = sourceProfiles.QueryElementAt(0, Components.interfaces.nsISupportsString);
+      if (sourceProfiles && sourceProfiles.length == 1) {
+        var profileName = sourceProfiles.queryElementAt(0, Ci.nsISupportsString);
         this._selectedProfile = profileName.data;
       }
       else
         this._selectedProfile = "";
     }
   },
   
   // 2 - [Profile Selection]
@@ -206,20 +206,19 @@ var MigrationWizard = {
     var profiles = document.getElementById("profiles");
     while (profiles.hasChildNodes()) 
       profiles.removeChild(profiles.firstChild);
     
     // Note that this block is still reached even if the user chose 'From File'
     // and we canceled the dialog.  When that happens, _migrator will be null.
     if (this._migrator) {
       var sourceProfiles = this._migrator.sourceProfiles;
-      var count = sourceProfiles.Count();
-      for (var i = 0; i < count; ++i) {
+      for (var i = 0; i < sourceProfiles.length; ++i) {
         var item = document.createElement("radio");
-        var str = sourceProfiles.QueryElementAt(i, Components.interfaces.nsISupportsString);
+        var str = sourceProfiles.queryElementAt(i, Ci.nsISupportsString);
         item.id = str.data;
         item.setAttribute("label", str.data);
         profiles.appendChild(item);
       }
     }
     
     profiles.selectedItem = this._selectedProfile ? document.getElementById(this._selectedProfile) : profiles.firstChild;
   },
--- a/browser/components/migration/public/nsIBrowserProfileMigrator.idl
+++ b/browser/components/migration/public/nsIBrowserProfileMigrator.idl
@@ -31,20 +31,20 @@
  * 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 "nsISupports.idl"
 
-interface nsISupportsArray;
+interface nsIArray;
 interface nsIProfileStartup;
 
-[scriptable, uuid(f8365b4a-da55-4e47-be7a-230142360f62)]
+[scriptable, uuid(5f445759-86a8-4dd3-ab84-4fc5341d9d9d)]
 interface nsIBrowserProfileMigrator : nsISupports 
 {
   /**
    * profile items to migrate. use with migrate().
    */
   const unsigned short ALL         = 0x0000;
   const unsigned short SETTINGS    = 0x0001;
   const unsigned short COOKIES     = 0x0002;
@@ -63,17 +63,18 @@ interface nsIBrowserProfileMigrator : ns
   void migrate(in unsigned short aItems, in nsIProfileStartup aStartup, in wstring aProfile);
 
   /**
    * A bit field containing profile items that this migrator
    * offers for import. 
    * @param   aProfile the profile that we are looking for available data
    *          to import
    * @param   aDoingStartup "true" if the profile is not currently being used.
-   * @returns bit field containing profile items (see above)
+   * @return  bit field containing profile items (see above)
+   * @note    a return value of 0 represents no items rather than ALL.
    */
   unsigned short getMigrateData(in wstring aProfile, in boolean aDoingStartup);
 
   /** 
    * Whether or not there is any data that can be imported from this 
    * browser (i.e. whether or not it is installed, and there exists
    * a user profile)
    */
@@ -84,15 +85,15 @@ interface nsIBrowserProfileMigrator : ns
    * has multiple user profiles configured.
    */
   readonly attribute boolean          sourceHasMultipleProfiles;
 
   /** 
    * An enumeration of available profiles. If the import source does 
    * not support profiles, this attribute is null.
    */
-  readonly attribute nsISupportsArray sourceProfiles;
-  
+  readonly attribute nsIArray         sourceProfiles;
+
   /**
    * The import source homepage.  Returns null if not present/available
    */
   readonly attribute AUTF8String      sourceHomePageURL;
 };
--- a/browser/components/migration/src/ChromeProfileMigrator.js
+++ b/browser/components/migration/src/ChromeProfileMigrator.js
@@ -34,16 +34,17 @@
  * 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 ***** */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
+const Cr = Components.results;
 
 const LOCAL_FILE_CID = "@mozilla.org/file/local;1";
 const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
 
 const BUNDLE_MIGRATION = "chrome://browser/locale/migration/migration.properties";
 
 const MIGRATE_ALL = 0x0000;
 const MIGRATE_SETTINGS = 0x0001;
@@ -67,31 +68,31 @@ XPCOMUtils.defineLazyGetter(this, "bookm
   let strbundle =
     Services.strings.createBundle(BUNDLE_MIGRATION);
   let sourceNameChrome = strbundle.GetStringFromName("sourceNameChrome");
   return strbundle.formatStringFromName("importedBookmarksFolder",
                                         [sourceNameChrome],
                                         1);
 });
 
-/*
+/**
  * Convert Chrome time format to Date object
  *
  * @param   aTime
  *          Chrome time 
  * @return  converted Date object
  * @note    Google Chrome uses FILETIME / 10 as time.
  *          FILETIME is based on same structure of Windows.
  */
 function chromeTimeToDate(aTime)
 {
   return new Date((aTime * S100NS_PER_MS - S100NS_FROM1601TO1970 ) / 10000);
 }
 
-/*
+/**
  * Insert bookmark items into specific folder.
  *
  * @param   aFolderId
  *          id of folder where items will be inserted
  * @param   aItems
  *          bookmark items to be inserted
  */
 function insertBookmarkItems(aFolderId, aItems)
@@ -124,51 +125,64 @@ function ChromeProfileMigrator()
 }
 
 ChromeProfileMigrator.prototype = {
   _paths: {
     bookmarks : null,
     cookies : null,
     history : null,
     prefs : null,
+    userData : null,
   },
 
   _homepageURL : null,
   _replaceBookmarks : false,
+  _sourceProfile: null,
+  _profilesCache: null,
 
-  /*
+  /**
    * Notify to observers to start migration
    *
    * @param   aType
    *          notification type such as MIGRATE_BOOKMARKS
    */
-
   _notifyStart : function Chrome_notifyStart(aType)
   {
     Services.obs.notifyObservers(null, "Migration:ItemBeforeMigrate", aType);
     this._pendingCount++;
   },
 
-  /*
+  /**
+   * Notify observers that a migration error occured with an item
+   *
+   * @param   aType
+   *          notification type such as MIGRATE_BOOKMARKS
+   */
+  _notifyError : function Chrome_notifyError(aType)
+  {
+    Services.obs.notifyObservers(null, "Migration:ItemError", aType);
+  },
+
+  /**
    * Notify to observers to finish migration for item
    * If all items are finished, it sends migration end notification.
    *
    * @param   aType
    *          notification type such as MIGRATE_BOOKMARKS
    */
   _notifyCompleted : function Chrome_notifyIfCompleted(aType)
   {
     Services.obs.notifyObservers(null, "Migration:ItemAfterMigrate", aType);
     if (--this._pendingCount == 0) {
       // All items are migrated, so we have to send end notification.
       Services.obs.notifyObservers(null, "Migration:Ended", null);
     }
   },
 
-  /*
+  /**
    * Migrating bookmark items
    */
   _migrateBookmarks : function Chrome_migrateBookmarks()
   {
     this._notifyStart(MIGRATE_BOOKMARKS);
 
     try {
       PlacesUtils.bookmarks.runInBatchMode({
@@ -219,21 +233,22 @@ ChromeProfileMigrator.prototype = {
             }
 
             migrator._notifyCompleted(MIGRATE_BOOKMARKS);
           });
         }
       }, null);
     } catch (e) {
       Cu.reportError(e);
+      this._notifyError(MIGRATE_BOOKMARKS);
       this._notifyCompleted(MIGRATE_BOOKMARKS);
     }
   },
 
-  /*
+  /**
    * Migrating history
    */
   _migrateHistory : function Chrome_migrateHistory()
   {
     this._notifyStart(MIGRATE_HISTORY);
 
     try {
       PlacesUtils.history.runInBatchMode({
@@ -293,21 +308,22 @@ ChromeProfileMigrator.prototype = {
               this._self._notifyCompleted(MIGRATE_HISTORY);
             }
           });
           stmt.finalize();
         }
       }, null);
     } catch (e) {
       Cu.reportError(e);
+      this._notifyError(MIGRATE_HISTORY);
       this._notifyCompleted(MIGRATE_HISTORY);
     }
   },
 
-  /*
+  /**
    * Migrating cookies
    */
   _migrateCookies : function Chrome_migrateCookies()
   {
     this._notifyStart(MIGRATE_COOKIES);
 
     try {
       // Access sqlite3 database of Chrome's cookie
@@ -354,41 +370,44 @@ ChromeProfileMigrator.prototype = {
         handleCompletion : function(aReason) {
           this._db.asyncClose();
           this._self._notifyCompleted(MIGRATE_COOKIES);
         },
       });
       stmt.finalize();
     } catch (e) {
       Cu.reportError(e);
+      this._notifyError(MIGRATE_COOKIES);
       this._notifyCompleted(MIGRATE_COOKIES);
     }
   },
 
-  /*
+  /**
    * nsIBrowserProfileMigrator interface implementation
    */
 
-  /*
+  /**
    * Let's migrate all items
    *
    * @param   aItems
-   *          list of data items to migrate.  but this is unused.
+   *          list of data items to migrate.
    * @param   aStartup
    *          non-null if called during startup.
    * @param   aProfile
-   *          this is unused due to single profile support only
+   *          profile directory name to migrate
    */
   migrate : function Chrome_migrate(aItems, aStartup, aProfile)
   {
     if (aStartup) {
       aStartup.doStartup();
       this._replaceBookmarks = true;
     }
 
+    this._sourceProfile = aProfile;
+
     Services.obs.notifyObservers(null, "Migration:Started", null);
 
     // Reset panding count.  If this count becomes 0, "Migration:Ended"
     // notification is sent
     this._pendingCount = 1;
 
     if (aItems & MIGRATE_HISTORY)
       this._migrateHistory();
@@ -402,112 +421,174 @@ ChromeProfileMigrator.prototype = {
     if (--this._pendingCount == 0) {
       // When async imports are immeditelly completed unfortunately,
       // this will be called.
       // Usually, this notification is sent by _notifyCompleted()
       Services.obs.notifyObservers(null, "Migration:Ended", null);
     }
   },
 
-  /*
+  /**
    * return supported migration types
    *
    * @param   aProfile
-   *          this is unused due to single profile support only
+   *          directory name of the profile
    * @param   aDoingStartup
    *          non-null if called during startup.
    * @return  supported migration types
    */
   getMigrateData: function Chrome_getMigrateData(aProfile, aDoingStartup)
   {
-#ifdef XP_WIN
-    let chromepath = Services.dirsvc.get("LocalAppData", Ci.nsIFile).path +
-                     "\\Google\\Chrome\\User Data\\Default\\";
-#elifdef XP_MACOSX
-    let chromepath = Services.dirsvc.get("Home", Ci.nsIFile).path +
-                     "/Library/Application Support/Google/Chrome/Default/";
-#else
-    let chromepath = Services.dirsvc.get("Home", Ci.nsIFile).path +
-                     "/.config/google-chrome/Default/";
-#endif
+    this._sourceProfile = aProfile;
+    let chromeProfileDir = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
+    chromeProfileDir.initWithPath(this._paths.userData + aProfile);
 
     let result = 0;
+    if (!chromeProfileDir.exists() || !chromeProfileDir.isReadable())
+      return result;
 
     // bookmark and preference are JSON format
 
     try {
-      let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
-      file.initWithPath(chromepath + "Bookmarks");
+      let file = chromeProfileDir.clone();
+      file.append("Bookmarks");
       if (file.exists()) {
-        this._paths.bookmarks = file.path
+        this._paths.bookmarks = file.path;
         result += MIGRATE_BOOKMARKS;
       }
     } catch (e) {
       Cu.reportError(e);
     }
 
-    if (!this._paths.prefs)
-      this._paths.prefs = chromepath + "Preferences";
+    if (!this._paths.prefs) {
+      let file = chromeProfileDir.clone();
+      file.append("Preferences");
+      this._paths.prefs = file.path;
+    }
 
     // history and cookies are SQLite database
 
     try {
-      let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
-      file.initWithPath(chromepath + "History");
+      let file = chromeProfileDir.clone();
+      file.append("History");
       if (file.exists()) {
-        this._paths.history = file.path
+        this._paths.history = file.path;
         result += MIGRATE_HISTORY;
       }
     } catch (e) {
       Cu.reportError(e);
     }
 
     try {
-      let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
-      file.initWithPath(chromepath + "Cookies");
+      let file = chromeProfileDir.clone();
+      file.append("Cookies");
       if (file.exists()) {
-        this._paths.cookies = file.path
+        this._paths.cookies = file.path;
         result += MIGRATE_COOKIES;
       }
     } catch (e) {
       Cu.reportError(e);
     }
 
     return result;
   },
 
-  /*
+  /**
    * Whether we support migration of Chrome
    *
    * @return true if supported
    */
   get sourceExists()
   {
-    let result = this.getMigrateData(null, false);
-    return result != 0;
+#ifdef XP_WIN
+    this._paths.userData = Services.dirsvc.get("LocalAppData", Ci.nsIFile).path +
+                            "\\Google\\Chrome\\User Data\\";
+#elifdef XP_MACOSX
+    this._paths.userData = Services.dirsvc.get("Home", Ci.nsIFile).path +
+                            "/Library/Application Support/Google/Chrome/";
+#else
+    this._paths.userData = Services.dirsvc.get("Home", Ci.nsIFile).path +
+                            "/.config/google-chrome/";
+#endif
+    let result = 0;
+    try {
+      let userDataDir = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
+      userDataDir.initWithPath(this._paths.userData);
+      if (!userDataDir.exists() || !userDataDir.isReadable())
+        return false;
+
+      let profiles = this.sourceProfiles;
+      if (profiles.length < 1)
+        return false;
+
+      // check that we can actually get data from the first profile
+      result = this.getMigrateData(profiles.queryElementAt(0, Ci.nsISupportsString), false);
+    } catch (e) {
+      Cu.reportError(e);
+    }
+    return result > 0;
+  },
+
+  get sourceHasMultipleProfiles()
+  {
+    return this.sourceProfiles.length > 1;
   },
 
-  // Although Chrome supports multi-profiles, there is no way
-  // to get profile lists.
-  sourceHasMultipleProfiles: false,
-  sourceProfiles: null,
+  get sourceProfiles()
+  {
+    let profiles = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+    try {
+      if (!this._profilesCache) {
+        let localState = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
+        // Local State is a JSON file that contains profile info.
+        localState.initWithPath(this._paths.userData + "Local State");
+        if (!localState.exists())
+          throw new Components.Exception("Chrome's 'Local State' file does not exist.",
+                                         Cr.NS_ERROR_FILE_NOT_FOUND);
+        if (!localState.isReadable())
+          throw new Components.Exception("Chrome's 'Local State' file could not be read.",
+                                         Cr.NS_ERROR_FILE_ACCESS_DENIED);
+        let fstream = Cc[FILE_INPUT_STREAM_CID].createInstance(Ci.nsIFileInputStream);
+        fstream.init(localState, -1, 0, 0);
+        let inputStream = NetUtil.readInputStreamToString(fstream, fstream.available(),
+                                                          { charset: "UTF-8" });
+        this._profilesCache = JSON.parse(inputStream).profile.info_cache;
+      }
 
-  /*
+      for (let index in this._profilesCache) {
+        let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+        str.data = index;
+        profiles.appendElement(str, false);
+      }
+    } catch (e) {
+      Cu.reportError("Error detecting Chrome profiles: " + e);
+      // if we weren't able to detect any profiles above, fallback to the Default profile.
+      if (profiles.length < 1) {
+        let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+        // the default profile name is "Default"
+        str.data = "Default";
+        profiles.appendElement(str, false);
+      }
+    }
+    return profiles;
+  },
+
+  /**
    * Return home page URL
    *
    * @return  home page URL
    */
   get sourceHomePageURL()
   {
     try  {
       if (this._homepageURL)
         return this._homepageURL;
 
       if (!this._paths.prefs)
-        this.getMigrateData(null, false);
+        this.getMigrateData(this._sourceProfile, false);
 
       // XXX reading and parsing JSON is synchronous.
       let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
       file.initWithPath(this._paths.prefs);
       let fstream = Cc[FILE_INPUT_STREAM_CID].
                     createInstance(Ci.nsIFileInputStream);
       fstream.init(file, -1, 0, 0); 
       this._homepageURL = JSON.parse(
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
@@ -49,17 +49,16 @@
 
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPrimitives.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIRDFService.h"
 #include "nsIStringBundle.h"
-#include "nsISupportsArray.h"
 #include "nsXPCOMCID.h"
 
 #define MIGRATION_BUNDLE "chrome://browser/locale/migration/migration.properties"
 
 #define BOOKMARKS_FILE_NAME NS_LITERAL_STRING("bookmarks.html")
 
 void SetUnicharPref(const char* aPref, const nsAString& aValue,
                     nsIPrefBranch* aPrefs)
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -60,17 +60,16 @@
 #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 "nsISupportsArray.h"
 #include "nsIProfileMigrator.h"
 #include "nsIBrowserProfileMigrator.h"
 #include "nsIObserverService.h"
 #include "nsILocalFileWin.h"
 #include "nsAutoPtr.h"
 
 #include "prnetdb.h"
 
@@ -481,17 +480,17 @@ nsIEProfileMigrator::GetSourceExists(boo
 NS_IMETHODIMP
 nsIEProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
 {
   *aResult = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsIEProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult)
+nsIEProfileMigrator::GetSourceProfiles(nsIArray** aResult)
 {
   *aResult = nsnull;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsIEProfileMigrator::GetSourceHomePageURL(nsACString& aResult)
 {
--- a/browser/components/migration/src/nsOperaProfileMigrator.cpp
+++ b/browser/components/migration/src/nsOperaProfileMigrator.cpp
@@ -165,56 +165,56 @@ nsOperaProfileMigrator::GetMigrateData(c
                           aReplace, mOperaProfile, aResult);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOperaProfileMigrator::GetSourceExists(bool* aResult)
 {
-  nsCOMPtr<nsISupportsArray> profiles;
+  nsCOMPtr<nsIArray> profiles;
   GetSourceProfiles(getter_AddRefs(profiles));
 
   if (profiles) { 
     PRUint32 count;
-    profiles->Count(&count);
+    profiles->GetLength(&count);
     *aResult = count > 0;
   }
   else
     *aResult = false;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOperaProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
 {
-  nsCOMPtr<nsISupportsArray> profiles;
+  nsCOMPtr<nsIArray> profiles;
   GetSourceProfiles(getter_AddRefs(profiles));
 
 #ifdef XP_WIN
   if (profiles) {
     PRUint32 count;
-    profiles->Count(&count);
+    profiles->GetLength(&count);
     *aResult = count > 1;
   }
   else
 #endif
     *aResult = false;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsOperaProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult)
+nsOperaProfileMigrator::GetSourceProfiles(nsIArray** aResult)
 {
   if (!mProfiles) {
     nsresult rv;
 
-    mProfiles = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
+    mProfiles = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     if (NS_FAILED(rv)) return rv;
 
     nsCOMPtr<nsIProperties> fileLocator(do_GetService("@mozilla.org/file/directory_service;1"));
     nsCOMPtr<nsILocalFile> file;
 #ifdef XP_WIN
     fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
 
     // Opera profile lives under %APP_DATA%\Opera\<operaver>\profile 
@@ -233,47 +233,47 @@ nsOperaProfileMigrator::GetSourceProfile
 
       bool isDirectory = false;
       curr->IsDirectory(&isDirectory);
       if (isDirectory) {
         nsCOMPtr<nsISupportsString> string(do_CreateInstance("@mozilla.org/supports-string;1"));
         nsAutoString leafName;
         curr->GetLeafName(leafName);
         string->SetData(leafName);
-        mProfiles->AppendElement(string);
+        mProfiles->AppendElement(string, false);
       }
 
       e->HasMoreElements(&hasMore);
     }
 #elif defined (XP_MACOSX)
     fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
     
     file->Append(NS_LITERAL_STRING("Preferences"));
     file->Append(OPERA_PREFERENCES_FOLDER_NAME);
     
     bool exists;
     file->Exists(&exists);
     
     if (exists) {
       nsCOMPtr<nsISupportsString> string(do_CreateInstance("@mozilla.org/supports-string;1"));
       string->SetData(OPERA_PREFERENCES_FOLDER_NAME);
-      mProfiles->AppendElement(string);
+      mProfiles->AppendElement(string, false);
     }
 #elif defined (XP_UNIX)
     fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(file));
     
     file->Append(OPERA_PREFERENCES_FOLDER_NAME);
     
     bool exists;
     file->Exists(&exists);
     
     if (exists) {
       nsCOMPtr<nsISupportsString> string(do_CreateInstance("@mozilla.org/supports-string;1"));
       string->SetData(OPERA_PREFERENCES_FOLDER_NAME);
-      mProfiles->AppendElement(string);
+      mProfiles->AppendElement(string, false);
     }
 #endif
   }
 
   *aResult = mProfiles;
   NS_IF_ADDREF(*aResult);
   return NS_OK;
 }
--- a/browser/components/migration/src/nsOperaProfileMigrator.h
+++ b/browser/components/migration/src/nsOperaProfileMigrator.h
@@ -37,19 +37,19 @@
  
 #ifndef operaprofilemigrator___h___
 #define operaprofilemigrator___h___
 
 #include "nsCOMPtr.h"
 #include "nsIBinaryInputStream.h"
 #include "nsIBrowserProfileMigrator.h"
 #include "nsIObserverService.h"
-#include "nsISupportsArray.h"
 #include "nsStringAPI.h"
 #include "nsTArray.h"
+#include "nsIMutableArray.h"
 #include "nsINavHistoryService.h"
 #include "nsIStringBundle.h"
 
 class nsICookieManager2;
 class nsILineInputStream;
 class nsILocalFile;
 class nsINIParser;
 class nsIPermissionManager;
@@ -139,17 +139,17 @@ protected:
                              nsIStringBundle* aBundle, 
                              PRInt64 aParentFolder);
 #endif // defined(XP_WIN) || (defined(XP_UNIX) && !defined(XP_MACOSX))
 
   void     GetOperaProfile(const PRUnichar* aProfile, nsILocalFile** aFile);
 
 private:
   nsCOMPtr<nsILocalFile> mOperaProfile;
-  nsCOMPtr<nsISupportsArray> mProfiles;
+  nsCOMPtr<nsIMutableArray> mProfiles;
   nsCOMPtr<nsIObserverService> mObserverService;
 };
 
 class nsOperaCookieMigrator
 {
 public:
   nsOperaCookieMigrator(nsIInputStream* aSourceStream);
   virtual ~nsOperaCookieMigrator();
--- a/browser/components/migration/src/nsProfileMigrator.cpp
+++ b/browser/components/migration/src/nsProfileMigrator.cpp
@@ -40,17 +40,17 @@
 #include "nsIBrowserProfileMigrator.h"
 #include "nsIComponentManager.h"
 #include "nsIDOMWindow.h"
 #include "nsILocalFile.h"
 #include "nsIObserverService.h"
 #include "nsIProperties.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsPrimitives.h"
-#include "nsISupportsArray.h"
+#include "nsIMutableArray.h"
 #include "nsIToolkitProfile.h"
 #include "nsIToolkitProfileService.h"
 #include "nsIWindowWatcher.h"
 
 #include "nsCOMPtr.h"
 #include "nsBrowserCompsCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDirectoryServiceDefs.h"
@@ -122,23 +122,22 @@ nsProfileMigrator::Migrate(nsIProfileSta
   nsCOMPtr<nsISupportsCString> cstr
     (do_CreateInstance("@mozilla.org/supports-cstring;1"));
   if (!cstr) return NS_ERROR_OUT_OF_MEMORY;
   cstr->SetData(key);
 
   // By opening the Migration FE with a supplied bpm, it will automatically
   // migrate from it. 
   nsCOMPtr<nsIWindowWatcher> ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-  nsCOMPtr<nsISupportsArray> params = 
-    do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
+  nsCOMPtr<nsIMutableArray> params = do_CreateInstance(NS_ARRAY_CONTRACTID);
   if (!ww || !params) return NS_ERROR_FAILURE;
 
-  params->AppendElement(cstr);
-  params->AppendElement(bpm);
-  params->AppendElement(aStartup);
+  params->AppendElement(cstr, false);
+  params->AppendElement(bpm, false);
+  params->AppendElement(aStartup, false);
 
   nsCOMPtr<nsIDOMWindow> migrateWizard;
   return ww->OpenWindow(nsnull, 
                         MIGRATION_WIZARD_FE_URL,
                         "_blank",
                         MIGRATION_WIZARD_FE_FEATURES,
                         params,
                         getter_AddRefs(migrateWizard));
--- a/browser/components/migration/src/nsSafariProfileMigrator.cpp
+++ b/browser/components/migration/src/nsSafariProfileMigrator.cpp
@@ -54,17 +54,16 @@
 #include "nsIProfileMigrator.h"
 #include "nsIProtocolHandler.h"
 #include "nsIRDFContainer.h"
 #include "nsIRDFDataSource.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsIRDFService.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
-#include "nsISupportsArray.h"
 #include "nsISupportsPrimitives.h"
 #include "nsSafariProfileMigrator.h"
 #include "nsToolkitCompsCID.h"
 #include "nsNetUtil.h"
 #include "nsTArray.h"
 
 #include <Carbon/Carbon.h>
 
@@ -197,17 +196,17 @@ NS_IMETHODIMP
 nsSafariProfileMigrator::GetSourceHasMultipleProfiles(bool* aResult)
 {
   // Safari only has one profile per-user.
   *aResult = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSafariProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult)
+nsSafariProfileMigrator::GetSourceProfiles(nsIArray** aResult)
 {
   *aResult = nsnull;
   return NS_OK;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // nsSafariProfileMigrator
 
--- a/browser/components/migration/src/nsSafariProfileMigrator.h
+++ b/browser/components/migration/src/nsSafariProfileMigrator.h
@@ -36,17 +36,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef safariprofilemigrator___h___
 #define safariprofilemigrator___h___
 
 #include "nsIBrowserProfileMigrator.h"
 #include "nsIObserverService.h"
-#include "nsISupportsArray.h"
 #include "nsStringAPI.h"
 #include "nsINavHistoryService.h"
 
 #include <CoreFoundation/CoreFoundation.h>
 
 class nsIRDFDataSource;
 
 class nsSafariProfileMigrator : public nsIBrowserProfileMigrator,
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_pageinfo.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_pageinfo.js
@@ -94,9 +94,10 @@ function test() {
       gBrowser.removeCurrentTab();
       gBrowser.removeCurrentTab();
 
       finish();
     });
   });
 
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
@@ -40,16 +40,17 @@
 
 function test() {
   // initialization
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
   let cm = Cc["@mozilla.org/cookiemanager;1"].
            getService(Ci.nsICookieManager);
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   const TEST_URL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/title.sjs";
 
   function waitForCleanup(aCallback) {
     // delete all cookies
     cm.removeAll();
     // delete all history items
     waitForClearHistory(aCallback);
--- a/browser/components/sessionstore/test/browser/browser_467409-backslashplosion.js
+++ b/browser/components/sessionstore/test/browser/browser_467409-backslashplosion.js
@@ -13,16 +13,17 @@
 //
 // 3.  [backwards compat] Use a stringified state as formdata when opening about:sessionrestore
 // 3a. Make sure there are nodes in the tree on about:sessionrestore (skipped, checking formdata is sufficient)
 // 3b. Check that there are no backslashes in the formdata
 // 3c. Check that formdata (via getBrowserState) doesn't require JSON.parse
 
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]};
   let crashState = { windows: [{ tabs: [{ entries: [{ url: "about:mozilla" }] }]}]};
 
   let pagedata = { url: "about:sessionrestore",
                    formdata: { "#sessionData": crashState } };
   let state = { windows: [{ tabs: [{ entries: [pagedata] }] }] };
 
--- a/browser/components/sessionstore/test/browser/browser_586147.js
+++ b/browser/components/sessionstore/test/browser/browser_586147.js
@@ -39,16 +39,17 @@ function observeOneRestore(callback) {
   Services.obs.addObserver(function() {
     Services.obs.removeObserver(arguments.callee, topic, false);
     callback();
   }, topic, false);
 };
 
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   // There should be one tab when we start the test
   let [origTab] = gBrowser.visibleTabs;
   let hiddenTab = gBrowser.addTab();
 
   is(gBrowser.visibleTabs.length, 2, "should have 2 tabs before hiding");
   gBrowser.showOnlyTheseTabs([origTab]);
   is(gBrowser.visibleTabs.length, 1, "only 1 after hiding");
--- a/browser/components/sessionstore/test/browser/browser_607016.js
+++ b/browser/components/sessionstore/test/browser/browser_607016.js
@@ -47,16 +47,17 @@ function cleanup() {
   } catch (e) {}
   ss.setBrowserState(stateBackup);
   executeSoon(finish);
 }
 
 function test() {
   /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   // Set the pref to true so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
   Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -143,16 +143,17 @@ function GroupItem(listOfEls, options) {
     .html(html)
     .appendTo($container);
 
   this.$closeButton = iQ('<div>')
     .addClass('close')
     .click(function() {
       self.closeAll();
     })
+    .attr("title", tabviewString("groupItem.closeGroup"))
     .appendTo($container);
 
   // ___ Title
   this.$titleContainer = iQ('.title-container', this.$titlebar);
   this.$title = iQ('.name', this.$titlebar);
   this.$titleShield = iQ('.title-shield', this.$titlebar);
   this.setTitle(options.title);
 
@@ -193,31 +194,33 @@ function GroupItem(listOfEls, options) {
         (self.$title)[0].select();
         self._titleFocused = true;
       }
     })
     .mousedown(function(e) {
       e.stopPropagation();
     })
     .keypress(handleKeyPress)
-    .keyup(handleKeyUp);
+    .keyup(handleKeyUp)
+    .attr("title", tabviewString("groupItem.defaultName"));
 
   this.$titleShield
     .mousedown(function(e) {
       self.lastMouseDownTarget = (Utils.isLeftClick(e) ? e.target : null);
     })
     .mouseup(function(e) {
       var same = (e.target == self.lastMouseDownTarget);
       self.lastMouseDownTarget = null;
       if (!same)
         return;
 
       if (!self.isDragging)
         self.focusTitle();
-    });
+    })
+    .attr("title", tabviewString("groupItem.defaultName"));
 
   if (options.focusTitle)
     this.focusTitle();
 
   // ___ Stack Expander
   this.$expander = iQ("<div/>")
     .addClass("stackExpander")
     .appendTo($container)
@@ -899,16 +902,17 @@ GroupItem.prototype = Utils.extend(new I
       .attr("type", "button")
       .attr("data-group-id", this.id)
       .appendTo("body");
     iQ("<span/>")
       .text(tabviewString("groupItem.undoCloseGroup"))
       .appendTo(this.$undoContainer);
     let undoClose = iQ("<span/>")
       .addClass("close")
+      .attr("title", tabviewString("groupItem.discardClosedGroup"))
       .appendTo(this.$undoContainer);
 
     this.$undoContainer.css({
       left: this.bounds.left + this.bounds.width/2 - iQ(self.$undoContainer).width()/2,
       top:  this.bounds.top + this.bounds.height/2 - iQ(self.$undoContainer).height()/2,
       "-moz-transform": "scale(.1)",
       opacity: 0
     });
@@ -1138,17 +1142,17 @@ GroupItem.prototype = Utils.extend(new I
 
       // if a blank tab is selected while restoring a tab the blank tab gets
       // removed. we need to keep the group alive for the restored tab.
       if (item.isRemovedAfterRestore)
         options.dontClose = true;
 
       let closed = options.dontClose ? false : this.closeIfEmpty();
       if (closed ||
-          (this._children.length == 0 && !gBrowser.selectedTab.pinned &&
+          (this._children.length == 0 && !gBrowser._numPinnedTabs &&
            !item.isDragging)) {
         this._makeLastActiveGroupItemActive();
       } else if (!options.dontArrange) {
         this.arrange({animate: !options.immediately});
         this._unfreezeItemSize({dontArrange: true});
       }
 
       this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item });
@@ -2225,39 +2229,41 @@ let GroupItems = {
               };
   
               new GroupItem([], Utils.extend({}, data, options));
             }
           }
         }
 
         toClose.forEach(function(groupItem) {
-          // All remaining children in to-be-closed groups are re-used by
-          // session restore. Reconnect them so that they're put into their
-          // right groups.
-          let children = groupItem.getChildren().concat();
+          // all tabs still existing in closed groups will be moved to new
+          // groups. prepare them to be reconnected later.
+          groupItem.getChildren().forEach(function (tabItem) {
+            if (tabItem.parent.hidden)
+              iQ(tabItem.container).show();
 
-          children.forEach(function (tabItem) {
-            if (tabItem.parent && tabItem.parent.hidden)
-              iQ(tabItem.container).show();
+            tabItem._reconnected = false;
 
             // sanity check the tab's groupID
             let tabData = Storage.getTabData(tabItem.tab);
-            let parentGroup = GroupItems.groupItem(tabData.groupID);
+
+            if (tabData) {
+              let parentGroup = GroupItems.groupItem(tabData.groupID);
 
-            // correct the tab's groupID if necessary
-            if (!parentGroup || -1 < toClose.indexOf(parentGroup)) {
-              tabData.groupID = activeGroupId || Object.keys(groupItemData)[0];
-              Storage.saveTab(tabItem.tab, tabData);
+              // the tab's group id could be invalid or point to a non-existing
+              // group. correct it by assigning the active group id or the first
+              // group of the just restored session.
+              if (!parentGroup || -1 < toClose.indexOf(parentGroup)) {
+                tabData.groupID = activeGroupId || Object.keys(groupItemData)[0];
+                Storage.saveTab(tabItem.tab, tabData);
+              }
             }
-
-            tabItem._reconnected = false;
-            tabItem._reconnect();
           });
 
+          // this closes the group but not its children
           groupItem.close({immediately: true});
         });
       }
 
       // set active group item
       if (activeGroupId) {
         let activeGroupItem = this.groupItem(activeGroupId);
         if (activeGroupItem)
@@ -2461,20 +2467,24 @@ let GroupItems = {
 
     let tabItems = this._activeGroupItem._children;
     gBrowser.showOnlyTheseTabs(tabItems.map(function(item) item.tab));
   },
 
   // ----------
   // Function: updateActiveGroupItemAndTabBar
   // Sets active TabItem and GroupItem, and updates tab bar appropriately.
-  updateActiveGroupItemAndTabBar: function GroupItems_updateActiveGroupItemAndTabBar(tabItem) {
+  // Parameters:
+  // tabItem - the tab item
+  // options - is passed to UI.setActive() directly
+  updateActiveGroupItemAndTabBar: 
+    function GroupItems_updateActiveGroupItemAndTabBar(tabItem, options) {
     Utils.assertThrow(tabItem && tabItem.isATabItem, "tabItem must be a TabItem");
 
-    UI.setActive(tabItem);
+    UI.setActive(tabItem, options);
     this._updateTabBar();
   },
 
   // ----------
   // Function: getNextGroupItemTab
   // Paramaters:
   //  reverse - the boolean indicates the direction to look for the next groupItem.
   // Returns the <tabItem>. If nothing is found, return null.
--- a/browser/components/tabview/search.js
+++ b/browser/components/tabview/search.js
@@ -426,23 +426,25 @@ let Search = {
     iQ("#search").hide();
     iQ("#searchshade").hide().mousedown(function Search_init_shade_mousedown(event) {
       if (event.target.id != "searchbox" && !self._blockClick)
         self.hide();
     });
 
     iQ("#searchbox").keyup(function Search_init_box_keyup() {
       self.perform();
-    });
+    })
+    .attr("title", tabviewString("button.searchTabs"));
 
     iQ("#searchbutton").mousedown(function Search_init_button_mousedown() {
       self._initiatedBy = "buttonclick";
       self.ensureShown();
       self.switchToInMode();
-    });
+    })
+    .attr("title", tabviewString("button.searchTabs"));
 
     window.addEventListener("focus", function Search_init_window_focus() {
       if (self.isEnabled()) {
         self._blockClick = true;
         setTimeout(function() {
           self._blockClick = false;
         }, 0);
       }
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -143,16 +143,18 @@ function TabItem(tab, options) {
     } else {
       if (!Items.item(this).isDragging)
         self.zoomIn();
     }
   });
 
   this.droppable(true);
 
+  this.$close.attr("title", tabbrowserString("tabs.closeTab"));
+
   TabItems.register(this);
 
   // ___ reconnect to data from Storage
   if (!TabItems.reconnectingPaused())
     this._reconnect(options);
 };
 
 TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
@@ -199,17 +201,25 @@ TabItem.prototype = Utils.extend(new Ite
   //
   // Parameters:
   //   tabData - the tab data
   //   imageData - the image data
   showCachedData: function TabItem_showCachedData(tabData, imageData) {
     this._cachedImageData = imageData;
     this.$cachedThumb.attr("src", this._cachedImageData).show();
     this.$canvas.css({opacity: 0});
-    this.$tabTitle.text(tabData.title ? tabData.title : "");
+    let label = "";
+    let title;
+    if (tabData.title) {
+      label = tabData.title;
+      title = label + "\n" + tabData.url;
+    } else {
+      title = tabData.url;
+    }
+    this.$tabTitle.text(label).attr("title", title);
 
     this._sendToSubscribers("showingCachedData");
   },
 
   // ----------
   // Function: hideCachedData
   // Hides the cached data i.e. image and title and show the canvas.
   hideCachedData: function TabItem_hideCachedData() {
@@ -998,20 +1008,20 @@ let TabItems = {
 
       // ___ remove from waiting list now that we have no other
       // early returns
       this._tabsWaitingForUpdate.remove(tab);
 
       // ___ URL
       let tabUrl = tab.linkedBrowser.currentURI.spec;
       if (tabUrl != tabItem.url) {
-        let oldURL = tabItem.url;
         tabItem.url = tabUrl;
         tabItem.save();
       }
+      tabItem.$container.attr("title", label + "\n" + tabUrl);
 
       // ___ Make sure the tab is complete and ready for updating.
       if (!this.isComplete(tab) && (!options || !options.force)) {
         // If it's incomplete, stick it on the end of the queue
         this._tabsWaitingForUpdate.push(tab);
         return;
       }
 
--- a/browser/components/tabview/tabview.js
+++ b/browser/components/tabview/tabview.js
@@ -8,18 +8,23 @@ const Cr = Components.results;
 Cu.import("resource:///modules/tabview/utils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "tabviewBundle", function() {
   return Services.strings.
     createBundle("chrome://browser/locale/tabview.properties");
 });
+XPCOMUtils.defineLazyGetter(this, "tabbrowserBundle", function() {
+  return Services.strings.
+    createBundle("chrome://browser/locale/tabbrowser.properties");
+});
 
 function tabviewString(name) tabviewBundle.GetStringFromName('tabview.' + name);
+function tabbrowserString(name) tabbrowserBundle.GetStringFromName(name);
 
 XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
   return Services.prefs.getBranch("browser.panorama.");
 });
 
 XPCOMUtils.defineLazyGetter(this, "gPrivateBrowsing", function() {
   return Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -82,18 +82,19 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug600645.js \
                  browser_tabview_bug600812.js \
                  browser_tabview_bug602432.js \
                  browser_tabview_bug604098.js \
                  browser_tabview_bug606657.js \
                  browser_tabview_bug606905.js \
                  browser_tabview_bug607108.js \
                  browser_tabview_bug608037.js \
+                 browser_tabview_bug608153.js \
+                 browser_tabview_bug608158.js \
                  browser_tabview_bug608184.js \
-                 browser_tabview_bug608158.js \
                  browser_tabview_bug608405.js \
                  browser_tabview_bug610208.js \
                  browser_tabview_bug610242.js \
                  browser_tabview_bug612470.js \
                  browser_tabview_bug613541.js \
                  browser_tabview_bug616729.js \
                  browser_tabview_bug616967.js \
                  browser_tabview_bug618816.js \
@@ -160,16 +161,18 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug677310.js \
                  browser_tabview_bug679853.js \
                  browser_tabview_bug681599.js \
                  browser_tabview_bug685476.js \
                  browser_tabview_bug685692.js \
                  browser_tabview_bug686654.js \
                  browser_tabview_bug697390.js \
                  browser_tabview_bug705621.js \
+                 browser_tabview_bug706430.js \
+                 browser_tabview_bug706736.js \
                  browser_tabview_click_group.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
                  browser_tabview_firstrun_pref.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug608153.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  let pinnedTab = gBrowser.addTab();
+  gBrowser.pinTab(pinnedTab);
+
+  registerCleanupFunction(function() {
+    gBrowser.unpinTab(pinnedTab);
+    while (gBrowser.tabs[1])
+      gBrowser.removeTab(gBrowser.tabs[1]);
+    hideTabView();
+  });
+
+  showTabView(function() {
+    let cw = TabView.getContentWindow();
+    let groupItemOne = cw.GroupItems.groupItems[0];
+    let groupItemTwo = createGroupItemWithBlankTabs(window, 250, 250, 40, 1);
+
+    is(cw.GroupItems.groupItems.length, 2, "Two group items");
+
+    hideTabView(function() {
+      gBrowser.selectedTab = pinnedTab;
+      is(cw.GroupItems.getActiveGroupItem(), groupItemTwo, "Group two is active");
+      is(gBrowser.selectedTab, pinnedTab, "Selected tab is the pinned tab");
+
+      goToNextGroup();
+      is(cw.GroupItems.getActiveGroupItem(), groupItemOne, "Group one is active");
+      is(gBrowser.selectedTab, pinnedTab, "Selected tab is the pinned tab");
+
+      finish();
+    });
+  });
+}
+
+function goToNextGroup() {
+  let utils =
+    QueryInterface(Ci.nsIInterfaceRequestor).
+      getInterface(Ci.nsIDOMWindowUtils);
+
+  const masks = Ci.nsIDOMNSEvent;
+  let mval = 0;
+  mval |= masks.CONTROL_MASK;
+
+  utils.sendKeyEvent("keypress", 0, 96, mval);
+}
--- a/browser/components/tabview/test/browser_tabview_bug705621.js
+++ b/browser/components/tabview/test/browser_tabview_bug705621.js
@@ -10,17 +10,17 @@ function test() {
     });
 
     let cw = win.TabView.getContentWindow();
 
     let groupItemOne = cw.GroupItems.groupItems[0];
     is(groupItemOne.getChildren().length, 1, "Group one has 1 tab item");
 
     let groupItemTwo = createGroupItemWithBlankTabs(win, 300, 300, 40, 1);
-    is(groupItemTwo.getChildren().length, 1, "Group two has 2 tab items");
+    is(groupItemTwo.getChildren().length, 1, "Group two has 1 tab item");
 
     whenTabViewIsHidden(function() {
       executeSoon(function() { 
         win.gBrowser.removeTab(win.gBrowser.selectedTab);
         is(cw.UI.getActiveTab(), groupItemOne.getChild(0), "TabItem in Group one is selected");
         finish();
       });
     }, win);
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug706430.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let state1 = {
+  windows: [{
+    tabs: [{
+      entries: [{ url: "about:blank#1" }],
+      hidden: true,
+      extData: {"tabview-tab": '{"url":"about:blank#1","groupID":1,"bounds":{"left":120,"top":20,"width":20,"height":20}}'}
+    },{
+      entries: [{ url: "about:blank#2" }],
+      hidden: false,
+      extData: {"tabview-tab": '{"url":"about:blank#2","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
+    }],
+    selected: 2,
+    extData: {
+      "tabview-groups": '{"nextID":3,"activeGroupId":2, "totalNumber":2}',
+      "tabview-group":
+        '{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},"id":1},' +
+        '"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},"id":2}}'
+    }
+  }]
+};
+
+let state2 = {
+  windows: [{
+    tabs: [{entries: [{ url: "about:blank#1" }], hidden: true},
+           {entries: [{ url: "about:blank#2" }], hidden: false}],
+    selected: 2
+  }]
+};
+
+let ss = Cc["@mozilla.org/browser/sessionstore;1"]
+         .getService(Ci.nsISessionStore);
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithState(state1, function (win) {
+    registerCleanupFunction(function () win.close());
+
+    showTabView(function () {
+      let cw = win.TabView.getContentWindow();
+      let [group1, group2] = cw.GroupItems.groupItems;
+      let [tab1, tab2] = win.gBrowser.tabs;
+
+      checkUrl(group1.getChild(0), "about:blank#1", "tab1 is in first group");
+      checkUrl(group2.getChild(0), "about:blank#2", "tab2 is in second group");
+
+      whenWindowStateReady(win, function () {
+        let groups = cw.GroupItems.groupItems;
+        is(groups.length, 1, "one groupItem");
+        is(groups[0].getChildren().length, 2, "single groupItem has two children");
+
+        finish();
+      });
+
+      ss.setWindowState(win, JSON.stringify(state2), true);
+    }, win);
+  });
+}
+
+function checkUrl(aTabItem, aUrl, aMsg) {
+  is(aTabItem.tab.linkedBrowser.currentURI.spec, aUrl, aMsg);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug706736.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function(win) {
+    registerCleanupFunction(function() {
+      win.close();
+    });
+
+    let cw = win.TabView.getContentWindow();
+
+    let groupItemOne = cw.GroupItems.groupItems[0];
+    is(groupItemOne.getChildren().length, 1, "Group one has 1 tab item");
+
+    let groupItemTwo = createGroupItemWithBlankTabs(win, 300, 300, 40, 1);
+    is(groupItemTwo.getChildren().length, 1, "Group two has 1 tab items");
+
+    whenTabViewIsHidden(function() {
+      win.gBrowser.removeTab(win.gBrowser.selectedTab);
+      executeSoon(function() {
+        win.undoCloseTab();
+
+        groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) {
+          groupItemTwo.removeSubscriber("childAdded", onChildAdded);
+
+          is(groupItemOne.getChildren().length, 1, "Group one still has 1 tab item");
+          is(groupItemTwo.getChildren().length, 1, "Group two still has 1 tab item");
+        });
+
+        finish();
+      });
+    }, win);
+    groupItemTwo.getChild(0).zoomIn();
+  }, function(win) {
+    let newTab = win.gBrowser.addTab();
+    win.gBrowser.pinTab(newTab);
+  });
+}
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -190,17 +190,18 @@ let UI = {
 
       // ___ currentTab
       this._currentTab = gBrowser.selectedTab;
 
       // ___ exit button
       iQ("#exit-button").click(function() {
         self.exit();
         self.blurAll();
-      });
+      })
+      .attr("title", tabviewString("button.exitTabGroups"));
 
       // When you click on the background/empty part of TabView,
       // we create a new groupItem.
       iQ(gTabViewFrame.contentDocument).mousedown(function(e) {
         if (iQ(":focus").length > 0) {
           iQ(":focus").each(function(element) {
             // don't fire blur event if the same input element is clicked.
             if (e.target != element && element.nodeName == "INPUT")
@@ -460,17 +461,18 @@ let UI = {
   // options
   //  dontSetActiveTabInGroup bool for not setting active tab in group
   setActive: function UI_setActive(item, options) {
     Utils.assert(item, "item must be given");
 
     if (item.isATabItem) {
       if (item.parent)
         GroupItems.setActiveGroupItem(item.parent);
-      this._setActiveTab(item);
+      if (!options || !options.dontSetActiveTabInGroup)
+        this._setActiveTab(item);
     } else {
       GroupItems.setActiveGroupItem(item);
       if (!options || !options.dontSetActiveTabInGroup) {
         let activeTab = item.getActiveTab()
         if (activeTab)
           this._setActiveTab(activeTab);
       }
     }
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -2215,17 +2215,19 @@ InspectorProgressListener.prototype = {
           GetStringFromName("confirmNavigationAway.buttonLeave"),
         accessKey: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"),
         callback: function onButtonLeave() {
           if (aRequest) {
             aRequest.resume();
             aRequest = null;
             this.IUI.closeInspectorUI();
+            return true;
           }
+          return false;
         }.bind(this),
       },
       {
         id: "inspector.confirmNavigationAway.buttonStay",
         label: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonStay"),
         accessKey: this.IUI.strings.
           GetStringFromName("confirmNavigationAway.buttonStayAccesskey"),
--- a/browser/devtools/highlighter/test/browser_inspector_bug_665880.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_665880.js
@@ -1,15 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   let doc;
   let objectNode;
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
--- a/browser/devtools/highlighter/test/browser_inspector_bug_690361.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_690361.js
@@ -122,16 +122,17 @@ function finishInspectorTests()
     gBrowser.removeCurrentTab();
     finish();
   });
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
     waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic tests for inspector";
--- a/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js
+++ b/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js
@@ -106,16 +106,17 @@ function inspectorRuleTrap()
   Services.obs.removeObserver(inspectorRuleTrap,
     InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false);
   is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1");
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   tab1 = gBrowser.addTab();
   gBrowser.selectedTab = tab1;
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
       true);
     waitForFocus(inspectorTabOpen1, content);
   }, true);
--- a/browser/devtools/highlighter/test/browser_inspector_infobar.js
+++ b/browser/devtools/highlighter/test/browser_inspector_infobar.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   let doc;
   let nodes;
   let cursor;
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
--- a/browser/devtools/highlighter/test/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -213,16 +213,17 @@ function finishInspectorTests()
 
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
     waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic tests for inspector";
--- a/browser/devtools/highlighter/test/browser_inspector_registertools.js
+++ b/browser/devtools/highlighter/test/browser_inspector_registertools.js
@@ -208,16 +208,17 @@ function finishUp() {
   gBrowser.removeCurrentTab();
   InspectorUI.initTools = initToolsMethod;
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
     waitForFocus(createDocument, content);
   }, true);
   
   content.location = "data:text/html,registertool tests for inspector";
--- a/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
+++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
@@ -132,16 +132,17 @@ function ruleViewOpened2()
   gBrowser.removeCurrentTab();
   InspectorUI.closeInspectorUI();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   tab1 = gBrowser.addTab();
   gBrowser.selectedTab = tab1;
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
       true);
     waitForFocus(inspectorTabOpen1, content);
   }, true);
--- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
+++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
@@ -258,16 +258,17 @@ function inspectorTabUnload1(evt)
   InspectorUI.closeInspectorUI();
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   tab1 = gBrowser.addTab();
   gBrowser.selectedTab = tab1;
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
       true);
     waitForFocus(inspectorTabOpen1, content);
   }, true);
--- a/browser/devtools/highlighter/test/browser_inspector_treeSelection.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treeSelection.js
@@ -100,16 +100,17 @@ function finishUp() {
   doc = h1 = null;
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     doc = content.document;
     waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic tests for inspector";
--- a/browser/devtools/styleinspector/test/browser/browser_bug683672.js
+++ b/browser/devtools/styleinspector/test/browser/browser_bug683672.js
@@ -9,16 +9,17 @@ let stylePanel;
 
 const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser/browser_bug683672.html";
 
 Cu.import("resource:///modules/devtools/CssHtmlTree.jsm");
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded()
 {
   browser.removeEventListener("load", tabLoaded, true);
   doc = content.document;
--- a/browser/devtools/styleinspector/test/browser/browser_styleinspector.js
+++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector.js
@@ -74,16 +74,17 @@ function finishUp()
   doc = stylePanel = null;
   gBrowser.removeCurrentTab();
   finish();
 }
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
     doc = content.document;
     waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic style inspector tests";
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_580030_errors_after_page_reload.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_580030_errors_after_page_reload.js
@@ -38,16 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that errors still show up in the Web Console after a page reload.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-error.html";
 
 function test() {
+  expectUncaughtException();
   addTab(TEST_URI);
   browser.addEventListener("load", onLoad, true);
 }
 
 // see bug 580030: the error handler fails silently after page reload.
 // https://bugzilla.mozilla.org/show_bug.cgi?id=580030
 function onLoad(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_582201_duplicate_errors.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_582201_duplicate_errors.js
@@ -39,29 +39,31 @@
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that exceptions thrown by content don't show up twice in the Web
 // Console.
 
 const TEST_DUPLICATE_ERROR_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-duplicate-error.html";
 
 function test() {
+  expectUncaughtException();
   addTab(TEST_DUPLICATE_ERROR_URI);
   browser.addEventListener("DOMContentLoaded", testDuplicateErrors, false);
 }
 
 function testDuplicateErrors() {
   browser.removeEventListener("DOMContentLoaded", testDuplicateErrors,
                               false);
   openConsole();
 
   HUDService.getHudByWindow(content).jsterm.clearOutput();
 
   Services.console.registerListener(consoleObserver);
 
+  expectUncaughtException();
   content.location.reload();
 }
 
 var consoleObserver = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   observe: function (aMessage)
   {
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_597136_external_script_errors.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_597136_external_script_errors.js
@@ -24,16 +24,17 @@ function tabLoaded(aEvent) {
   browser.addEventListener("load", contentLoaded, true);
   content.location.reload();
 }
 
 function contentLoaded(aEvent) {
   browser.removeEventListener("load", contentLoaded, true);
 
   let button = content.document.querySelector("button");
+  expectUncaughtException();
   EventUtils.sendMouseEvent({ type: "click" }, button, content);
   executeSoon(buttonClicked);
 }
 
 function buttonClicked() {
   let outputNode = HUDService.getHudByWindow(content).outputNode;
 
   let msg = "the error from the external script was logged";
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_597756_reopen_closed_tab.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_597756_reopen_closed_tab.js
@@ -13,16 +13,17 @@ const TEST_URI = "http://example.com/bro
 let newTabIsOpen = false;
 
 function tabLoaded(aEvent) {
   gBrowser.selectedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
 
   HUDService.activateHUDForContext(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", tabReloaded, true);
+  expectUncaughtException();
   content.location.reload();
 }
 
 function tabReloaded(aEvent) {
   gBrowser.selectedBrowser.removeEventListener(aEvent.type, arguments.callee, true);
 
   let hudId = HUDService.getHudIdByWindow(content);
   let HUD = HUDService.hudReferences[hudId];
@@ -38,22 +39,24 @@ function tabReloaded(aEvent) {
     }
 
     let newTab = gBrowser.addTab();
     gBrowser.removeCurrentTab();
     gBrowser.selectedTab = newTab;
 
     newTabIsOpen = true;
     gBrowser.selectedBrowser.addEventListener("load", tabLoaded, true);
+    expectUncaughtException();
     content.location = TEST_URI;
   });
 }
 
 function testEnd() {
   gBrowser.removeCurrentTab();
   executeSoon(finishTest);
 }
 
 function test() {
+  expectUncaughtException();
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_601177_log_levels.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_601177_log_levels.js
@@ -59,12 +59,13 @@ function test()
     browser.removeEventListener(aEvent.type, arguments.callee, true);
 
     openConsole();
 
     browser.addEventListener("load", function(aEvent) {
       browser.removeEventListener(aEvent.type, arguments.callee, true);
       executeSoon(onContentLoaded);
     }, true);
+    expectUncaughtException();
     content.location = TEST_URI;
   }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_618078_network_exceptions.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_618078_network_exceptions.js
@@ -85,13 +85,14 @@ function test()
 
     let hudId = HUDService.getHudIdByWindow(content);
     hud = HUDService.hudReferences[hudId];
 
     Services.console.registerListener(TestObserver);
     registerCleanupFunction(testEnd);
 
     executeSoon(function() {
+      expectUncaughtException();
       content.location = TEST_URI;
     });
   }, true);
 }
 
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_644419_log_limits.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_644419_log_limits.js
@@ -20,16 +20,17 @@ function test() {
 
 function onLoad(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
 
   openConsole();
 
   gHudId = HUDService.getHudIdByWindow(content);
   browser.addEventListener("load", testWebDevLimits, true);
+  expectUncaughtException();
   content.location = TEST_URI;
 }
 
 function testWebDevLimits(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
   gOldPref = Services.prefs.getIntPref("devtools.hud.loglimit.console");
   Services.prefs.setIntPref("devtools.hud.loglimit.console", 10);
 
@@ -65,16 +66,17 @@ function testJsLimits(aEvent) {
 
   // Find the sentinel entry.
   findLogEntry("testing JS limits");
   // Fill the log with JS errors.
   let head = content.document.getElementsByTagName("head")[0];
   for (let i = 0; i < 11; i++) {
     var script = content.document.createElement("script");
     script.text = "fubar" + i + ".bogus(6);";
+    expectUncaughtException();
     head.insertBefore(script, head.firstChild);
   }
 
   executeSoon(function() {
     testLogEntry(outputNode, "fubar0 is not defined", "first message is pruned", false, true);
     findLogEntry("fubar1 is not defined");
     // Check if the sentinel entry is still there.
     findLogEntry("testing JS limits");
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_network_panel.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_network_panel.js
@@ -412,17 +412,20 @@ function testGen() {
 
   // Test a response with a content type that can't be displayed in the
   // NetworkPanel.
   httpActivity.response.header["Content-Type"] = "application/x-shockwave-flash";
 
   networkPanel = HUDService.openNetworkPanel(filterBox, httpActivity);
   networkPanel.isDoneCallback = function NP_doneCallback() {
     networkPanel.isDoneCallback = null;
-    testDriver.next();
+    try {
+      testDriver.next();
+    } catch (e if e instanceof StopIteration) {
+    }
   }
 
   yield;
 
   checkIsVisible(networkPanel, {
     requestBody: false,
     requestFormData: true,
     requestCookie: true,
@@ -473,10 +476,10 @@ function testGen() {
   if (networkPanel.document.getElementById("responseBodyUnknownTypeContent").textContent !== "")
     checkNodeContent(networkPanel, "responseBodyUnknownTypeContent", responseString);
   else
     ok(true, "Flash not installed");
 
   networkPanel.panel.hidePopup(); */
 
   // All done!
-  finishTest();
+  finish();
 }
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_view_source.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_view_source.js
@@ -2,16 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that source URLs in the Web Console can be clicked to display the
 // standard View Source window.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-error.html";
 
 function test() {
+  expectUncaughtException();
   addTab(TEST_URI);
   browser.addEventListener("DOMContentLoaded", testViewSource, false);
 }
 
 function testViewSource() {
   browser.removeEventListener("DOMContentLoaded", testViewSource, false);
 
   openConsole();
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -1309,34 +1309,34 @@ xpicleanup@BIN_SUFFIX@
 #endif
 #ifdef XP_WIN
   components/brwsrcmp.dll
   components/jsd3250.dll
   components/nsPostUpdateWin.js
   js3250.dll
   plugins/npnul32.dll
   #if _MSC_VER != 1400
-    @BINPATH@/Microsoft.VC80.CRT.manifest
-    @BINPATH@/msvcm80.dll
-    @BINPATH@/msvcp80.dll
-    @BINPATH@/msvcr80.dll
+    Microsoft.VC80.CRT.manifest
+    msvcm80.dll
+    msvcp80.dll
+    msvcr80.dll
   #endif
   #if _MSC_VER != 1500
-    @BINPATH@/Microsoft.VC90.CRT.manifest
-    @BINPATH@/msvcm90.dll
-    @BINPATH@/msvcp90.dll
-    @BINPATH@/msvcr90.dll
+    Microsoft.VC90.CRT.manifest
+    msvcm90.dll
+    msvcp90.dll
+    msvcr90.dll
   #endif
   #if _MSC_VER != 1600
-    @BINPATH@/msvcp100.dll
-    @BINPATH@/msvcr100.dll
+    msvcp100.dll
+    msvcr100.dll
   #endif
   #if _MSC_VER != 1700
-    @BINPATH@/msvcp110.dll
-    @BINPATH@/msvcr110.dll
+    msvcp110.dll
+    msvcr110.dll
   #endif
   mozcrt19.dll
   mozcpp19.dll
 #endif
 @DLL_PREFIX@xpcom_core@DLL_SUFFIX@
 components/@DLL_PREFIX@jar50@DLL_SUFFIX@
 #ifdef XP_WIN
   components/xpinstal.dll
--- a/browser/locales/en-US/chrome/browser/migration/migration.dtd
+++ b/browser/locales/en-US/chrome/browser/migration/migration.dtd
@@ -4,18 +4,16 @@
 <!ENTITY importFrom.label               "Import Options, Bookmarks, History, Passwords and other data from:">
 <!ENTITY importFromUnix.label           "Import Preferences, Bookmarks, History, Passwords and other data from:">
 <!ENTITY importFromBookmarks.label      "Import Bookmarks from:">
 
 <!ENTITY importFromIE.label             "Microsoft Internet Explorer">
 <!ENTITY importFromIE.accesskey         "M">
 <!ENTITY importFromNothing.label        "Don't import anything">
 <!ENTITY importFromNothing.accesskey    "D">
-<!ENTITY importFromSeamonkey.label      "Netscape 6, 7 or Mozilla 1.x">
-<!ENTITY importFromSeamonkey.accesskey  "N">
 <!ENTITY importFromOpera.label          "Opera">
 <!ENTITY importFromOpera.accesskey      "O">
 <!ENTITY importFromSafari.label         "Safari">
 <!ENTITY importFromSafari.accesskey     "S">
 <!ENTITY importFromChrome.label         "Chrome">
 <!ENTITY importFromChrome.accesskey     "C">
 <!ENTITY importFromHTMLFile.label       "From an HTML File">
 <!ENTITY importFromHTMLFile.accesskey   "F">
--- a/browser/locales/en-US/chrome/browser/migration/migration.properties
+++ b/browser/locales/en-US/chrome/browser/migration/migration.properties
@@ -1,62 +1,53 @@
 profileName_format=%S %S
 
 # Browser Specific
 sourceNameIE=Internet Explorer
-sourceNameSeamonkey=Netscape 6/7/Mozilla
 sourceNameOpera=Opera
 sourceNameSafari=Safari
 sourceNameChrome=Google Chrome
 
 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.
 
-importedSeamonkeyBookmarksTitle=From Netscape 6/7/Mozilla
 importedSafariBookmarks=From Safari
 importedOperaHotlistTitle=From Opera
 importedOperaSearchUrls=Keyword Searches (From Opera)
 
 # Import Sources
 1_ie=Internet Options
 1_opera=Preferences
-1_seamonkey=Preferences
 1_safari=Preferences
 1_chrome=Preferences
 
 2_ie=Cookies
 2_opera=Cookies
-2_seamonkey=Cookies
 2_safari=Cookies
 2_chrome=Cookies
 
 4_ie=Browsing History
 4_opera=Browsing History
-4_seamonkey=Browsing History
 4_safari=Browsing History
 4_chrome=Browsing History
 
 8_ie=Saved Form History
 8_opera=Saved Form History
-8_seamonkey=Saved Form History
 8_safari=Saved Form History
 8_chrome=Saved Form History
 
 16_ie=Saved Passwords
 16_opera=Saved Passwords
-16_seamonkey=Saved Passwords
 16_safari=Saved Passwords
 16_chrome=Saved Passwords
 
 32_ie=Favorites
 32_opera=Bookmarks
-32_seamonkey=Bookmarks
 32_safari=Bookmarks
 32_chrome=Bookmarks
 
 64_ie=Other Data
 64_opera=Other Data
-64_seamonkey=Other Data
 64_safari=Other Data
 64_chrome=Other Data
 
--- a/browser/locales/en-US/chrome/browser/tabview.properties
+++ b/browser/locales/en-US/chrome/browser/tabview.properties
@@ -1,4 +1,8 @@
-tabview.groupItem.defaultName=Name this tab group…
+tabview.button.searchTabs=Search tab groups
+tabview.button.exitTabGroups=Exit tab groups
+tabview.groupItem.defaultName=Name this tab group
+tabview.groupItem.closeGroup=Close group
 tabview.groupItem.undoCloseGroup=Undo Close Group
+tabview.groupItem.discardClosedGroup=Discard closed group
 tabview.search.otherWindowTabs=Tabs from other windows
 tabview.notification.sessionStore=Tabs and groups will automatically be restored the next time you start %S.
--- a/configure.in
+++ b/configure.in
@@ -4359,17 +4359,17 @@ dnl = If NSS was not detected in the sys
 dnl = use the one in the source tree (mozilla/security/nss)
 dnl ========================================================
 
 MOZ_ARG_WITH_BOOL(system-nss,
 [  --with-system-nss       Use system installed NSS],
     _USE_SYSTEM_NSS=1 )
 
 if test -n "$_USE_SYSTEM_NSS"; then
-    AM_PATH_NSS(3.13.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+    AM_PATH_NSS(3.13.2, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
    NSS_DEP_LIBS="\
         \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -1157,19 +1157,31 @@ WebGLContext::Notify(nsITimer* timer)
 }
 
 void
 WebGLContext::MaybeRestoreContext()
 {
     if (mContextLost || mAllowRestore)
         return;
 
-    gl->MakeCurrent();
-    GLContext::ContextResetARB resetStatus = 
-        (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
+    GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
+    if (mHasRobustness) {
+        gl->MakeCurrent();
+        resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
+    // This call is safe as it does not actually interact with GL, so the
+    // context does not have to be current.
+    } else if (gl->GetContextType() == GLContext::ContextTypeEGL) {
+        // Simulate a ARB_robustness guilty context loss for when we
+        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
+        // but we can't make any distinction, so we must assume the worst
+        // case.
+        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
+            resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
+        }
+    }
     
     if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
         // It's already lost, but clean up after it and signal to JS that it is
         // lost.
         ForceLoseContext();
     }
 
     switch (resetStatus) {
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -52,16 +52,17 @@
 
 #include "nsIDOMWebGLRenderingContext.h"
 #include "nsICanvasRenderingContextInternal.h"
 #include "nsHTMLCanvasElement.h"
 #include "nsWeakReference.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIMemoryReporter.h"
+#include "nsContentUtils.h"
 
 #include "GLContextProvider.h"
 #include "Layers.h"
 
 #include "CheckedInt.h"
 
 /* 
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
@@ -444,17 +445,17 @@ public:
     
     bool MinCapabilityMode() const {
         return mMinCapability;
     }
 
     // Sets up the GL_ARB_robustness timer if it isn't already, so that if the
     // driver gets restarted, the context may get reset with it.
     void SetupRobustnessTimer() {
-        if (mContextLost || !mHasRobustness)
+        if (mContextLost || (!mHasRobustness && gl->GetContextType() != gl::GLContext::ContextTypeEGL))
             return;
 
         // If the timer was already running, don't restart it here. Instead,
         // wait until the previous call is done, then fire it one more time.
         // This is an optimization to prevent unnecessary cross-communication
         // between threads.
         if (mRobustnessTimerRunning) {
             mDrawSinceRobustnessTimerSet = true;
--- a/content/html/document/src/VideoDocument.cpp
+++ b/content/html/document/src/VideoDocument.cpp
@@ -79,18 +79,16 @@ VideoDocument::StartDocumentLoad(const c
                                  nsIContentSink*     aSink)
 {
   nsresult rv =
     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
                                      aDocListener, aReset, aSink);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mStreamListener = new MediaDocumentStreamListener(this);
-  if (!mStreamListener)
-    return NS_ERROR_OUT_OF_MEMORY;
 
   // Create synthetic document
   rv = CreateSyntheticVideoDocument(aChannel,
       getter_AddRefs(mStreamListener->mNextStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ADDREF(*aDocListener = mStreamListener);
   return rv;
@@ -153,17 +151,16 @@ VideoDocument::UpdateTitle(nsIChannel* a
 
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewVideoDocument(nsIDocument** aResult)
 {
   mozilla::dom::VideoDocument* doc = new mozilla::dom::VideoDocument();
-  NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(doc);
   nsresult rv = doc->Init();
 
   if (NS_FAILED(rv)) {
     NS_RELEASE(doc);
   }
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1740,17 +1740,18 @@ nsHTMLDocument::Open(const nsAString& aC
 
     // Now make sure we're not flagged as the initial document anymore, now
     // that we've had stuff done to us.  From now on, if anyone tries to
     // document.open() us, they get a new inner window.
     SetIsInitialDocument(false);
 
     nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
     if (oldScope && newScope != oldScope) {
-      nsContentUtils::ReparentContentWrappersInScope(cx, oldScope, newScope);
+      rv = nsContentUtils::ReparentContentWrappersInScope(cx, oldScope, newScope);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // Call Reset(), this will now do the full reset
   Reset(channel, group);
   if (baseURI) {
     mDocumentBaseURI = baseURI;
   }
@@ -1949,16 +1950,18 @@ nsHTMLDocument::WriteCommon(JSContext *c
               1, getter_AddRefs(ignored));
 
     // If Open() fails, or if it didn't create a parser (as it won't
     // if the user chose to not discard the current document through
     // onbeforeunload), don't write anything.
     if (NS_FAILED(rv) || !mParser) {
       return rv;
     }
+    NS_ABORT_IF_FALSE(!JS_IsExceptionPending(cx),
+                      "Open() succeeded but JS exception is pending");
   }
 
   static NS_NAMED_LITERAL_STRING(new_line, "\n");
 
   // Save the data in cache
   if (mWyciwygChannel) {
     if (!aText.IsEmpty()) {
       mWyciwygChannel->WriteToCacheEntry(aText);
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -1072,17 +1072,17 @@ nsMediaCache::PredictNextUseForIncomingD
   }
   if (bytesAhead <= 0)
     return TimeDuration(0);
   PRInt64 millisecondsAhead = bytesAhead*1000/aStream->mPlaybackBytesPerSecond;
   return TimeDuration::FromMilliseconds(
       NS_MIN<PRInt64>(millisecondsAhead, PR_INT32_MAX));
 }
 
-enum StreamAction { NONE, SEEK, RESUME, SUSPEND };
+enum StreamAction { NONE, SEEK, SEEK_AND_RESUME, RESUME, SUSPEND };
 
 void
 nsMediaCache::Update()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   // The action to use for each stream. We store these so we can make
   // decisions while holding the cache lock but implement those decisions
@@ -1317,66 +1317,83 @@ nsMediaCache::Update()
       if (stream->mChannelOffset != desiredOffset && enableReading) {
         // We need to seek now.
         NS_ASSERTION(stream->mIsSeekable || desiredOffset == 0,
                      "Trying to seek in a non-seekable stream!");
         // Round seek offset down to the start of the block. This is essential
         // because we don't want to think we have part of a block already
         // in mPartialBlockBuffer.
         stream->mChannelOffset = (desiredOffset/BLOCK_SIZE)*BLOCK_SIZE;
-        actions[i] = SEEK;
+        actions[i] = stream->mCacheSuspended ? SEEK_AND_RESUME : SEEK;
       } else if (enableReading && stream->mCacheSuspended) {
         actions[i] = RESUME;
       } else if (!enableReading && !stream->mCacheSuspended) {
         actions[i] = SUSPEND;
       }
     }
 #ifdef DEBUG
     mInUpdate = false;
 #endif
   }
 
   // Update the channel state without holding our cache lock. While we're
   // doing this, decoder threads may be running and seeking, reading or changing
   // other cache state. That's OK, they'll trigger new Update events and we'll
   // get back here and revise our decisions. The important thing here is that
   // performing these actions only depends on mChannelOffset and
-  // mCacheSuspended, which can only be written by the main thread (i.e., this
+  // the action, which can only be written by the main thread (i.e., this
   // thread), so we don't have races here.
+
+  // First, update the mCacheSuspended/mCacheEnded flags so that they're all correct
+  // when we fire our CacheClient commands below. Those commands can rely on these flags
+  // being set correctly for all streams.
   for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
     nsMediaCacheStream* stream = mStreams[i];
-    nsresult rv = NS_OK;
     switch (actions[i]) {
     case SEEK:
-      LOG(PR_LOG_DEBUG, ("Stream %p CacheSeek to %lld (resume=%d)", stream,
-           (long long)stream->mChannelOffset, stream->mCacheSuspended));
-      rv = stream->mClient->CacheClientSeek(stream->mChannelOffset,
-                                            stream->mCacheSuspended);
+	case SEEK_AND_RESUME:
       stream->mCacheSuspended = false;
       stream->mChannelEnded = false;
       break;
+    case RESUME:
+      stream->mCacheSuspended = false;
+      break;
+    case SUSPEND:
+      stream->mCacheSuspended = true;
+      break;
+    default:
+      break;
+    }
+    stream->mHasHadUpdate = true;
+  }
 
+  for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
+    nsMediaCacheStream* stream = mStreams[i];
+    nsresult rv;
+    switch (actions[i]) {
+    case SEEK:
+	case SEEK_AND_RESUME:
+      LOG(PR_LOG_DEBUG, ("Stream %p CacheSeek to %lld (resume=%d)", stream,
+           (long long)stream->mChannelOffset, actions[i] == SEEK_AND_RESUME));
+      rv = stream->mClient->CacheClientSeek(stream->mChannelOffset,
+                                            actions[i] == SEEK_AND_RESUME);
+      break;
     case RESUME:
       LOG(PR_LOG_DEBUG, ("Stream %p Resumed", stream));
       rv = stream->mClient->CacheClientResume();
-      stream->mCacheSuspended = false;
       break;
-
     case SUSPEND:
       LOG(PR_LOG_DEBUG, ("Stream %p Suspended", stream));
       rv = stream->mClient->CacheClientSuspend();
-      stream->mCacheSuspended = true;
       break;
-
     default:
+      rv = NS_OK;
       break;
     }
 
-    stream->mHasHadUpdate = true;
-
     if (NS_FAILED(rv)) {
       // Close the streams that failed due to error. This will cause all
       // client Read and Seek operations on those streams to fail. Blocked
       // Reads will also be woken up.
       ReentrantMonitorAutoEnter mon(mReentrantMonitor);
       stream->CloseInternal(mon);
     }
   }
--- a/content/media/nsMediaStream.cpp
+++ b/content/media/nsMediaStream.cpp
@@ -761,17 +761,17 @@ nsMediaChannelStream::CacheClientNotifyD
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 nsMediaChannelStream::CacheClientSeek(PRInt64 aOffset, bool aResume)
 {
   NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
 
-  printf("*** nsMediaChannelStream::CacheClientSeek() mDecoder=%p\n", mDecoder);
+  printf("*** nsMediaChannelStream::CacheClientSeek() mDecoder=%p aOffset=%lld aResume = %d\n", mDecoder, (long long)aOffset, aResume);
 
   CloseChannel();
 
   if (aResume) {
     NS_ASSERTION(mSuspendCount > 0, "Too many resumes!");
     // No need to mess with the channel, since we're making a new one
     --mSuspendCount;
   }
--- a/dbm/src/Makefile.in
+++ b/dbm/src/Makefile.in
@@ -74,16 +74,17 @@ endif
 ifeq (,$(filter -DHAVE_SNPRINTF=1,$(ACDEFINES)))
 CSRCS += snprintf.c
 endif
 endif # WINNT
 
 LOCAL_INCLUDES	= -I$(srcdir)/../include
 
 FORCE_STATIC_LIB = 1
+FORCE_USE_PIC = 1
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES		+= -DMEMMOVE -D__DBINTERFACE_PRIVATE $(SECURITY_FLAG)
 
 ifeq ($(OS_ARCH),WINCE)
 DEFINES     += -D__STDC__ -DDBM_REOPEN_ON_FLUSH
 endif
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/706283-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+
+window.mozRequestAnimationFrame(null);
+
+</script>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -27,8 +27,9 @@ load 601247.html
 load 609560-1.xhtml
 load 612018-1.html
 load 637116.html
 load 666869.html
 load 675621-1.html
 load 693894.html
 load 695867.html
 load 697643.html
+load 706283-1.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1322,16 +1322,17 @@ nsDOMWindowUtils::SendQueryContentEvent(
     nsQueryContentEvent dummyEvent(true, NS_QUERY_CONTENT_STATE, widget);
     InitEvent(dummyEvent, &pt);
     nsIFrame* popupFrame =
       nsLayoutUtils::GetPopupFrameForEventCoordinates(presContext->GetRootPresContext(), &dummyEvent);
 
     nsIntRect widgetBounds;
     nsresult rv = widget->GetClientBounds(widgetBounds);
     NS_ENSURE_SUCCESS(rv, rv);
+    widgetBounds.MoveTo(0, 0);
 
     // There is no popup frame at the point and the point isn't in our widget,
     // we cannot process this request.
     NS_ENSURE_TRUE(popupFrame || widgetBounds.Contains(pt),
                    NS_ERROR_FAILURE);
 
     // Fire the event on the widget at the point
     if (popupFrame) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3878,16 +3878,20 @@ nsGlobalWindow::MozRequestAnimationFrame
 {
   FORWARD_TO_INNER(MozRequestAnimationFrame, (aCallback),
                    NS_ERROR_NOT_INITIALIZED);
 
   if (!mDoc) {
     return NS_OK;
   }
 
+  if (!aCallback) {
+    return NS_ERROR_XPC_BAD_CONVERT_JS;
+  }
+
   mDoc->ScheduleFrameRequestCallback(aCallback);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetMozAnimationStartTime(PRInt64 *aTime)
 {
   FORWARD_TO_INNER(GetMozAnimationStartTime, (aTime), NS_ERROR_NOT_INITIALIZED);
@@ -10315,17 +10319,17 @@ nsGlobalChromeWindow::BeginWindowMove(ns
 #ifdef MOZ_XUL
   if (aPanel) {
     nsCOMPtr<nsIContent> panel = do_QueryInterface(aPanel);
     NS_ENSURE_TRUE(panel, NS_ERROR_FAILURE);
 
     nsIFrame* frame = panel->GetPrimaryFrame();
     NS_ENSURE_TRUE(frame && frame->GetType() == nsGkAtoms::menuPopupFrame, NS_OK);
 
-    (static_cast<nsMenuPopupFrame*>(frame))->GetWidget(getter_AddRefs(widget));
+    widget = (static_cast<nsMenuPopupFrame*>(frame))->GetWidget();
   }
   else {
 #endif
     widget = GetMainWidget();
 #ifdef MOZ_XUL
   }
 #endif
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -220,18 +220,17 @@ ContentParent::Init()
     }
     nsCOMPtr<nsIPrefBranch2> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
     if (prefs) {
         prefs->AddObserver("", this, false);
     }
     nsCOMPtr<nsIThreadInternal>
             threadInt(do_QueryInterface(NS_GetCurrentThread()));
     if (threadInt) {
-        threadInt->GetObserver(getter_AddRefs(mOldObserver));
-        threadInt->SetObserver(this);
+        threadInt->AddObserver(this);
     }
     if (obs) {
         obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", nsnull);
     }
 
 #ifdef ACCESSIBILITY
     // If accessibility is running in chrome process then start it in content
     // process.
@@ -339,17 +338,17 @@ ContentParent::ActorDestroy(ActorDestroy
     }
 
     RecvRemoveGeolocationListener();
     RecvRemoveDeviceMotionListener();
 
     nsCOMPtr<nsIThreadInternal>
         threadInt(do_QueryInterface(NS_GetCurrentThread()));
     if (threadInt)
-        threadInt->SetObserver(mOldObserver);
+        threadInt->RemoveObserver(this);
     if (mRunToCompletionDepth)
         mRunToCompletionDepth = 0;
 
     if (gContentParents) {
         gContentParents->RemoveElement(this);
         if (!gContentParents->Length()) {
             delete gContentParents;
             gContentParents = NULL;
@@ -1113,35 +1112,30 @@ ContentParent::RecvLoadURIExternal(const
     nsCOMPtr<nsIURI> ourURI(uri);
     extProtService->LoadURI(ourURI, nsnull);
     return true;
 }
 
 /* void onDispatchedEvent (in nsIThreadInternal thread); */
 NS_IMETHODIMP
 ContentParent::OnDispatchedEvent(nsIThreadInternal *thread)
-{
-    if (mOldObserver)
-        return mOldObserver->OnDispatchedEvent(thread);
-
-    return NS_OK;
+{
+   NS_NOTREACHED("OnDispatchedEvent unimplemented");
+   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* void onProcessNextEvent (in nsIThreadInternal thread, in boolean mayWait, in unsigned long recursionDepth); */
 NS_IMETHODIMP
 ContentParent::OnProcessNextEvent(nsIThreadInternal *thread,
                                   bool mayWait,
                                   PRUint32 recursionDepth)
 {
     if (mRunToCompletionDepth)
         ++mRunToCompletionDepth;
 
-    if (mOldObserver)
-        return mOldObserver->OnProcessNextEvent(thread, mayWait, recursionDepth);
-
     return NS_OK;
 }
 
 /* void afterProcessNextEvent (in nsIThreadInternal thread, in unsigned long recursionDepth); */
 NS_IMETHODIMP
 ContentParent::AfterProcessNextEvent(nsIThreadInternal *thread,
                                      PRUint32 recursionDepth)
 {
@@ -1151,19 +1145,16 @@ ContentParent::AfterProcessNextEvent(nsI
             printf("... ran to completion.\n");
 #endif
             if (mShouldCallUnblockChild) {
                 mShouldCallUnblockChild = false;
                 UnblockChild();
             }
     }
 
-    if (mOldObserver)
-        return mOldObserver->AfterProcessNextEvent(thread, recursionDepth);
-
     return NS_OK;
 }
 
 bool
 ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                          const nsString& aText, const bool& aTextClickable,
                                          const nsString& aCookie, const nsString& aName)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -230,17 +230,16 @@ private:
                                  const PRUint32& aFlags,
                                  const nsCString& aCategory);
 
     GeckoChildProcessHost* mSubprocess;
 
     PRInt32 mGeolocationWatchID;
     int mRunToCompletionDepth;
     bool mShouldCallUnblockChild;
-    nsCOMPtr<nsIThreadObserver> mOldObserver;
 
     // This is a cache of all of the memory reporters
     // registered in the child process.  To update this, one
     // can broadcast the topic "child-memory-reporter-request" using
     // the nsIObserverService.
     nsCOMArray<nsIMemoryReporter> mMemoryReporters;
 
     bool mIsAlive;
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -68,16 +68,17 @@ using mozilla::dom::StorageChild;
 #include "nsCycleCollectionParticipant.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIJSContextStack.h"
 #include "nsIPrivateBrowsingService.h"
 #include "nsDOMString.h"
 #include "nsNetCID.h"
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
+#include "mozilla/Telemetry.h"
 
 // calls FlushAndDeleteTemporaryTables(false)
 #define NS_DOMSTORAGE_FLUSH_TIMER_TOPIC "domstorage-flush-timer"
 
 // calls FlushAndDeleteTemporaryTables(true)
 #define NS_DOMSTORAGE_FLUSH_FORCE_TOPIC "domstorage-flush-force"
 
 using namespace mozilla;
@@ -1596,32 +1597,69 @@ nsDOMStorage::GetItem(const nsAString& a
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else
     SetDOMStringToNull(aData);
 
   return NS_OK;
 }
 
+static Telemetry::ID
+TelemetryIDForKey(nsPIDOMStorage::nsDOMStorageType type)
+{
+  switch (type) {
+  default:
+    MOZ_ASSERT(false);
+    // We need to return something to satisfy the compiler.
+    // Fallthrough.
+  case nsPIDOMStorage::GlobalStorage:
+    return Telemetry::GLOBALDOMSTORAGE_KEY_SIZE_BYTES;
+  case nsPIDOMStorage::LocalStorage:
+    return Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES;
+  case nsPIDOMStorage::SessionStorage:
+    return Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES;
+  }
+}
+
+static Telemetry::ID
+TelemetryIDForValue(nsPIDOMStorage::nsDOMStorageType type)
+{
+  switch (type) {
+  default:
+    MOZ_ASSERT(false);
+    // We need to return something to satisfy the compiler.
+    // Fallthrough.
+  case nsPIDOMStorage::GlobalStorage:
+    return Telemetry::GLOBALDOMSTORAGE_VALUE_SIZE_BYTES;
+  case nsPIDOMStorage::LocalStorage:
+    return Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES;
+  case nsPIDOMStorage::SessionStorage:
+    return Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES;
+  }
+}
+
 NS_IMETHODIMP
 nsDOMStorage::GetItem(const nsAString& aKey, nsIDOMStorageItem **aItem)
 {
   nsresult rv;
 
   NS_IF_ADDREF(*aItem = GetNamedItem(aKey, &rv));
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsDOMStorage::SetItem(const nsAString& aKey, const nsAString& aData)
 {
   if (!CacheStoragePermissions())
     return NS_ERROR_DOM_SECURITY_ERR;
 
+  Telemetry::Accumulate(TelemetryIDForKey(mStorageType), aKey.Length());
+  Telemetry::Accumulate(TelemetryIDForValue(mStorageType), aData.Length());
+
   nsString oldValue;
   nsresult rv = mStorageImpl->SetValue(IsCallerSecure(), aKey, aData, oldValue);
   if (NS_FAILED(rv))
     return rv;
 
   if ((oldValue != aData || mStorageType == GlobalStorage) && mEventBroadcaster)
     mEventBroadcaster->BroadcastChangeNotification(aKey, oldValue, aData);
 
--- a/dom/tests/unit/test_domstorage_aboutpages.js
+++ b/dom/tests/unit/test_domstorage_aboutpages.js
@@ -8,25 +8,44 @@ Components.utils.import("resource://gre/
 function run_test()
 {
   // Needs a profile folder for the database.
   do_get_profile();
   testURI(Services.io.newURI("about:mozilla", null, null));
   testURI(Services.io.newURI("moz-safe-about:rights", null, null));
 }
 
+function sum(a)
+{
+  return a.reduce(function(prev, current, index, array) {
+    return prev + current;
+  });
+}
+
 function testURI(aURI)
 {
   print("Testing: " + aURI.spec);
   let storage = getStorageForURI(aURI);
+  let Telemetry = Components.classes["@mozilla.org/base/telemetry;1"].
+                  getService(Components.interfaces.nsITelemetry);
+  let key_histogram = Telemetry.getHistogramById("LOCALDOMSTORAGE_KEY_SIZE_BYTES");
+  let value_histogram = Telemetry.getHistogramById("LOCALDOMSTORAGE_VALUE_SIZE_BYTES");
+  let before_key_snapshot = key_histogram.snapshot();
+  let before_value_snapshot = value_histogram.snapshot();
   storage.setItem("test-item", "test-value");
   print("Check that our value has been correctly stored.");
+  let after_key_snapshot = key_histogram.snapshot();
+  let after_value_snapshot = value_histogram.snapshot();
   do_check_eq(storage.length, 1);
   do_check_eq(storage.key(0), "test-item");
   do_check_eq(storage.getItem("test-item"), "test-value");
+  do_check_eq(sum(after_key_snapshot.counts),
+	      sum(before_key_snapshot.counts)+1);
+  do_check_eq(sum(after_value_snapshot.counts),
+	      sum(before_value_snapshot.counts)+1);
 
   print("Check that our value is correctly removed.");
   storage.removeItem("test-item");
   do_check_eq(storage.length, 0);
   do_check_eq(storage.getItem("test-item"), null);
 
   testURIWithPrivateBrowsing(aURI);
 
--- a/dom/workers/EventTarget.cpp
+++ b/dom/workers/EventTarget.cpp
@@ -134,16 +134,19 @@ EventTarget::FromJSObject(JSContext* aCx
   return GetPrivate(aCx, aObj);
 }
 
 // static
 JSBool
 EventTarget::AddEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+  if (!obj) {
+    return true;
+  }
 
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSString* type;
   JSObject* listener;
@@ -162,16 +165,19 @@ EventTarget::AddEventListener(JSContext*
                                                  capturing, wantsUntrusted);
 }
 
 // static
 JSBool
 EventTarget::RemoveEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+  if (!obj) {
+    return true;
+  }
 
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSString* type;
   JSObject* listener;
@@ -190,16 +196,19 @@ EventTarget::RemoveEventListener(JSConte
                                                     capturing);
 }
 
 // static
 JSBool
 EventTarget::DispatchEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
 {
   JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+  if (!obj) {
+    return true;
+  }
 
   EventTarget* self = GetPrivate(aCx, obj);
   if (!self) {
     return true;
   }
 
   JSObject* event;
   if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &event)) {
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -312,46 +312,55 @@ private:
     *aVp = INT_TO_JSVAL(JSID_TO_INT(idval));
     return true;
   }
 
   static JSBool
   StopPropagation(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     Event* event = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!event) {
       return false;
     }
 
     event->mStopPropagationCalled = true;
 
     return true;
   }
 
   static JSBool
   StopImmediatePropagation(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     Event* event = GetInstancePrivate(aCx, obj, sFunctions[3].name);
     if (!event) {
       return false;
     }
 
     event->mStopImmediatePropagationCalled = true;
 
     return true;
   }
   
   static JSBool
   PreventDefault(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     Event* event = GetInstancePrivate(aCx, obj, sFunctions[1].name);
     if (!event) {
       return false;
     }
 
     jsval cancelableVal;
     if (!GetPropertyCommon(aCx, obj, SLOT_cancelable, &cancelableVal)) {
@@ -362,16 +371,19 @@ private:
            JS_SetReservedSlot(aCx, obj, SLOT_defaultPrevented, cancelableVal) :
            true;
   }
 
   static JSBool
   InitEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     Event* event = GetInstancePrivate(aCx, obj, sFunctions[2].name);
     if (!event) {
       return false;
     }
 
     JSString* type;
     JSBool bubbles, cancelable;
@@ -622,16 +634,19 @@ private:
 
     return JS_GetReservedSlot(aCx, aObj, slot, aVp);
   }
 
   static JSBool
   InitMessageEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     MessageEvent* event = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!event) {
       return false;
     }
 
     JSString* type, *data, *origin;
     JSBool bubbles, cancelable;
@@ -818,16 +833,19 @@ private:
 
     return JS_GetReservedSlot(aCx, aObj, slot, aVp);
   }
 
   static JSBool
   InitErrorEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     ErrorEvent* event = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!event) {
       return false;
     }
 
     JSString* type, *message, *filename;
     JSBool bubbles, cancelable;
@@ -1005,16 +1023,19 @@ private:
 
     return JS_GetReservedSlot(aCx, aObj, slot, aVp);
   }
 
   static JSBool
   InitProgressEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     ProgressEvent* event = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!event) {
       return false;
     }
 
     JSString* type;
     JSBool bubbles, cancelable, lengthComputable;
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -117,16 +117,19 @@ private:
     JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
     delete GetJSPrivateSafeish<DOMException>(aCx, aObj);
   }
 
   static JSBool
   ToString(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     JSClass* classPtr;
     if (!obj || ((classPtr = JS_GET_CLASS(aCx, obj)) != &sClass)) {
       JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
                            JSMSG_INCOMPATIBLE_PROTO, sClass.name, "toString",
                            classPtr ? classPtr->name : "object");
       return false;
     }
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -180,16 +180,19 @@ private:
 
     return true;
   }
 
   static JSBool
   MozSlice(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "mozSlice");
     if (!blob) {
       return false;
     }
 
     jsdouble start = 0, end = 0;
     JSString* jsContentType = JS_GetEmptyString(JS_GetRuntime(aCx));
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -175,16 +175,19 @@ private:
       GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj);
     NS_IF_RELEASE(fileReader);
   }
 
   static JSBool
   ReadAsArrayBuffer(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     FileReaderSyncPrivate* fileReader =
       GetInstancePrivate(aCx, obj, "readAsArrayBuffer");
     if (!fileReader) {
       return false;
     }
 
     JSObject* jsBlob;
@@ -219,16 +222,19 @@ private:
     JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(jsArrayBuffer));
     return true;
   }
 
   static JSBool
   ReadAsDataURL(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     FileReaderSyncPrivate* fileReader =
       GetInstancePrivate(aCx, obj, "readAsDataURL");
     if (!fileReader) {
       return false;
     }
 
     JSObject* jsBlob;
@@ -256,16 +262,19 @@ private:
     JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
     return true;
   }
 
   static JSBool
   ReadAsBinaryString(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     FileReaderSyncPrivate* fileReader =
       GetInstancePrivate(aCx, obj, "readAsBinaryString");
     if (!fileReader) {
       return false;
     }
 
     JSObject* jsBlob;
@@ -293,16 +302,19 @@ private:
     JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
     return true;
   }
 
   static JSBool
   ReadAsText(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     FileReaderSyncPrivate* fileReader =
       GetInstancePrivate(aCx, obj, "readAsText");
     if (!fileReader) {
       return false;
     }
 
     JSObject* jsBlob;
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -258,30 +258,36 @@ private:
       worker->TraceInstance(aTrc);
     }
   }
 
   static JSBool
   Terminate(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     const char*& name = sFunctions[0].name;
     WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
     if (!worker) {
       return !JS_IsExceptionPending(aCx);
     }
 
     return worker->Terminate(aCx);
   }
 
   static JSBool
   PostMessage(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     const char*& name = sFunctions[1].name;
     WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
     if (!worker) {
       return !JS_IsExceptionPending(aCx);
     }
 
     jsval message;
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -373,29 +373,35 @@ private:
     *aVp = scope->mSlots[SLOT_navigator];
     return true;
   }
 
   static JSBool
   Close(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!scope) {
       return false;
     }
 
     return scope->mWorker->CloseInternal(aCx);
   }
 
   static JSBool
   ImportScripts(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[1].name);
     if (!scope) {
       return false;
     }
 
     if (aArgc && !scriptloader::Load(aCx, aArgc, JS_ARGV(aCx, aVp))) {
       return false;
@@ -403,16 +409,19 @@ private:
 
     return true;
   }
 
   static JSBool
   SetTimeout(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[2].name);
     if (!scope) {
       return false;
     }
 
     jsval dummy;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &dummy)) {
@@ -421,16 +430,19 @@ private:
 
     return scope->mWorker->SetTimeout(aCx, aArgc, aVp, false);
   }
 
   static JSBool
   ClearTimeout(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[3].name);
     if (!scope) {
       return false;
     }
 
     uint32 id;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "u", &id)) {
@@ -439,16 +451,19 @@ private:
 
     return scope->mWorker->ClearTimeout(aCx, id);
   }
 
   static JSBool
   SetInterval(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[4].name);
     if (!scope) {
       return false;
     }
 
     jsval dummy;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &dummy)) {
@@ -457,16 +472,19 @@ private:
 
     return scope->mWorker->SetTimeout(aCx, aArgc, aVp, true);
   }
 
   static JSBool
   ClearInterval(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[5].name);
     if (!scope) {
       return false;
     }
 
     uint32 id;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "u", &id)) {
@@ -474,18 +492,22 @@ private:
     }
 
     return scope->mWorker->ClearTimeout(aCx, id);
   }
 
   static JSBool
   Dump(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
-    if (!GetInstancePrivate(aCx, JS_THIS_OBJECT(aCx, aVp),
-                            sFunctions[6].name)) {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
+
+    if (!GetInstancePrivate(aCx, obj, sFunctions[6].name)) {
       return false;
     }
 
     if (aArgc) {
       JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
       if (!str) {
         return false;
       }
@@ -500,18 +522,22 @@ private:
     }
 
     return true;
   }
 
   static JSBool
   AtoB(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
-    if (!GetInstancePrivate(aCx, JS_THIS_OBJECT(aCx, aVp),
-                            sFunctions[7].name)) {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
+
+    if (!GetInstancePrivate(aCx, obj, sFunctions[7].name)) {
       return false;
     }
 
     jsval string;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &string)) {
       return false;
     }
 
@@ -522,18 +548,22 @@ private:
 
     JS_SET_RVAL(aCx, aVp, result);
     return true;
   }
 
   static JSBool
   BtoA(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
-    if (!GetInstancePrivate(aCx, JS_THIS_OBJECT(aCx, aVp),
-                            sFunctions[8].name)) {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
+
+    if (!GetInstancePrivate(aCx, obj, sFunctions[8].name)) {
       return false;
     }
 
     jsval binary;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &binary)) {
       return false;
     }
 
@@ -741,16 +771,19 @@ private:
       scope->TraceInstance(aTrc);
     }
   }
 
   static JSBool
   PostMessage(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     const char*& name = sFunctions[0].name;
     DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
     if (!scope) {
       return false;
     }
 
     jsval message;
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -600,30 +600,36 @@ private:
 
     return priv->SetEventListenerOnEventTarget(aCx, name + 2, aVp);
   }
 
   static JSBool
   Abort(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!priv) {
       return false;
     }
 
     return priv->Abort(aCx);
   }
 
   static JSBool
   GetAllResponseHeaders(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[1].name);
     if (!priv) {
       return false;
     }
 
     JSString* responseHeaders = priv->GetAllResponseHeaders(aCx);
@@ -634,16 +640,19 @@ private:
     JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(responseHeaders));
     return true;
   }
 
   static JSBool
   GetResponseHeader(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[2].name);
     if (!priv) {
       return false;
     }
 
     jsval headerVal;
@@ -670,16 +679,19 @@ private:
     JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(value));
     return true;
   }
 
   static JSBool
   Open(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[3].name);
     if (!priv) {
       return false;
     }
 
     JSString* method, *url;
@@ -693,32 +705,38 @@ private:
 
     return priv->Open(aCx, method, url, async, user, password);
   }
 
   static JSBool
   Send(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[4].name);
     if (!priv) {
       return false;
     }
 
     jsval body = aArgc ? JS_ARGV(aCx, aVp)[0] : JSVAL_VOID;
 
     return priv->Send(aCx, !!aArgc, body);
   }
 
   static JSBool
   SendAsBinary(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[5].name);
     if (!priv) {
       return false;
     }
 
     jsval bodyVal;
@@ -739,16 +757,19 @@ private:
 
     return priv->SendAsBinary(aCx, body);
   }
 
   static JSBool
   SetRequestHeader(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[6].name);
     if (!priv) {
       return false;
     }
 
     JSString* header, *value;
@@ -759,16 +780,19 @@ private:
 
     return priv->SetRequestHeader(aCx, header, value);
   }
 
   static JSBool
   OverrideMimeType(JSContext* aCx, uintN aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    if (!obj) {
+      return false;
+    }
 
     XMLHttpRequestPrivate* priv =
       GetInstancePrivate(aCx, obj, sFunctions[7].name);
     if (!priv) {
       return false;
     }
 
     JSString* mimeType;
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2493,24 +2493,26 @@ GLContext::SetBlitFramebufferForDestText
 
     fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer);
     fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                           LOCAL_GL_COLOR_ATTACHMENT0,
                           LOCAL_GL_TEXTURE_2D,
                           aTexture,
                           0);
 
-    if (aTexture && (fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) !=
-                     LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
-
+    GLenum result = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
+        nsCAutoString msg;
+        msg.Append("Framebuffer not complete -- error 0x");
+        msg.AppendInt(result, 16);
         // Note: if you are hitting this, it is likely that
         // your texture is not texture complete -- that is, you
         // allocated a texture name, but didn't actually define its
         // size via a call to TexImage2D.
-        NS_RUNTIMEABORT("Error setting up framebuffer --- framebuffer not complete!");
+        NS_RUNTIMEABORT(msg.get());
     }
 }
 
 #ifdef DEBUG
 
 void
 GLContext::CreatedProgram(GLContext *aOrigin, GLuint aName)
 {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -537,16 +537,17 @@ public:
         mIsOffscreen(aIsOffscreen),
 #ifdef USE_GLES2
         mIsGLES2(true),
 #else
         mIsGLES2(false),
 #endif
         mIsGlobalSharedContext(false),
         mHasRobustness(false),
+        mContextLost(false),
         mVendor(-1),
         mDebugMode(0),
         mCreationFormat(aFormat),
         mSharedContext(aSharedContext),
         mOffscreenTexture(0),
         mFlipped(false),
         mBlitProgram(0),
         mBlitFramebuffer(0),
@@ -590,16 +591,18 @@ public:
 
     bool MakeCurrent(bool aForce = false) {
 #ifdef DEBUG
         sCurrentGLContext = this;
 #endif
         return MakeCurrentImpl(aForce);
     }
 
+    bool IsContextLost() { return mContextLost; }
+
     virtual bool SetupLookupFunction() = 0;
 
     virtual void WindowDestroyed() {}
 
     virtual void ReleaseSurface() {}
 
     void *GetUserData(void *aKey) {
         void *result = nsnull;
@@ -1321,16 +1324,17 @@ public:
     }
 
 protected:
     bool mInitialized;
     bool mIsOffscreen;
     bool mIsGLES2;
     bool mIsGlobalSharedContext;
     bool mHasRobustness;
+    bool mContextLost;
 
     PRInt32 mVendor;
 
     enum {
         DebugEnabled = 1 << 0,
         DebugTrace = 1 << 1,
         DebugAbortOnError = 1 << 2
     };
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -829,16 +829,20 @@ public:
             mSurface = CreateSurfaceForWindow(NULL, config);
             aForce = true;
         }
 #endif
         if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) {
             succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
                                                  mSurface, mSurface,
                                                  mContext);
+            if (!succeeded && sEGLLibrary.fGetError() == LOCAL_EGL_CONTEXT_LOST) {
+                mContextLost = true;
+                NS_WARNING("EGL context has been lost.");
+            }
             NS_ASSERTION(succeeded, "Failed to make GL context current!");
         }
 
         return succeeded;
     }
 
 #ifdef MOZ_WIDGET_QT
     virtual bool
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -117,16 +117,23 @@ CreateDummyWindow(HDC *aWindowDC = nsnul
 
     if (aWindowDC) {
         *aWindowDC = dc;
     }
 
     return win;
 }
 
+static inline bool
+HasExtension(const char* aExtensions, const char* aRequiredExtension)
+{
+    return GLContext::ListHasExtension(
+        reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
+}
+
 bool
 WGLLibrary::EnsureInitialized()
 {
     if (mInitialized)
         return true;
 
     mozilla::ScopedGfxFeatureReporter reporter("WGL");
 
@@ -200,19 +207,57 @@ WGLLibrary::EnsureInitialized()
 
     if (!LibrarySymbolLoader::LoadSymbols(mOGLLibrary, &pixFmtSymbols[0],
          (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress))
     {
         // this isn't an error, just means that we don't have the pixel format extension
         fChoosePixelFormat = nsnull;
     }
 
+    LibrarySymbolLoader::SymLoadStruct extensionsSymbols[] = {
+        { (PRFuncPtr *) &fGetExtensionsString, { "wglGetExtensionsStringARB", NULL} },
+        { NULL, { NULL } }
+    };
+
+    LibrarySymbolLoader::SymLoadStruct robustnessSymbols[] = {
+        { (PRFuncPtr *) &fCreateContextAttribs, { "wglCreateContextAttribsARB", NULL} },
+        { NULL, { NULL } }
+    };
+
+    if (LibrarySymbolLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0],
+        (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress)) {
+        const char *wglExts = fGetExtensionsString(gSharedWindowDC);
+        if (wglExts && HasExtension(wglExts, "WGL_ARB_create_context")) {
+            LibrarySymbolLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0],
+            (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress);
+            if (HasExtension(wglExts, "WGL_ARB_create_context_robustness")) {
+                mHasRobustness = true;
+            }
+        }
+    }
+
     // reset back to the previous context, just in case
     fMakeCurrent(curDC, curCtx);
 
+    if (mHasRobustness) {
+        fDeleteContext(gSharedWindowGLContext);
+
+        int attribs[] = {
+            LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+            LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+            NULL
+        };
+
+        gSharedWindowGLContext = fCreateContextAttribs(gSharedWindowDC, NULL, attribs);
+        if (!gSharedWindowGLContext) {
+            mHasRobustness = false;
+            gSharedWindowGLContext = fCreateContext(gSharedWindowDC);
+        }
+    }
+
     mInitialized = true;
 
     // Call this to create the global GLContext instance,
     // and to check for errors.  Note that this must happen /after/
     // setting mInitialized to TRUE, or an infinite loop results.
     if (GLContextProviderWGL::GetGlobalContext() == nsnull) {
         mInitialized = false;
         return false;
@@ -304,17 +349,17 @@ public:
     }
 
     virtual bool IsDoubleBuffered() {
         return mIsDoubleBuffered;
     }
 
     bool SupportsRobustness()
     {
-        return false;
+        return sWGLLibrary.HasRobustness();
     }
 
     virtual bool SwapBuffers() {
         if (!mIsDoubleBuffered)
             return false;
         return ::SwapBuffers(mDC);
     }
 
@@ -497,26 +542,45 @@ GLContextProviderWGL::CreateForWindow(ns
        * We need to make sure we call SetPixelFormat -after- calling 
        * EnsureInitialized, otherwise it can load/unload the dll and 
        * wglCreateContext will fail.
        */
 
     HDC dc = (HDC)aWidget->GetNativeData(NS_NATIVE_GRAPHIC);
 
     SetPixelFormat(dc, gSharedWindowPixelFormat, NULL);
-    HGLRC context = sWGLLibrary.fCreateContext(dc);
-    if (!context) {
-        return nsnull;
+    HGLRC context;
+
+    GLContextWGL *shareContext = GetGlobalContextWGL();
+
+    if (sWGLLibrary.HasRobustness()) {
+        int attribs[] = {
+            LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+            LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+            NULL
+        };
+
+        context = sWGLLibrary.fCreateContextAttribs(dc,
+                                                    shareContext ? shareContext->Context() : nsnull,
+                                                    attribs);
+        if (!context && shareContext) {
+            context = sWGLLibrary.fCreateContextAttribs(dc, nsnull, attribs);
+            if (context) {
+                shareContext = nsnull;
+            }
+        } else {
+            context = sWGLLibrary.fCreateContext(dc);
+            if (context && shareContext && !sWGLLibrary.fShareLists(shareContext->Context(), context)) {
+                shareContext = nsnull;
+            }
+        }
     }
 
-    GLContextWGL *shareContext = GetGlobalContextWGL();
-    if (shareContext &&
-        !sWGLLibrary.fShareLists(shareContext->Context(), context))
-    {
-        shareContext = nsnull;
+    if (!context) {
+        return nsnull;
     }
 
     nsRefPtr<GLContextWGL> glContext = new GLContextWGL(ContextFormat(ContextFormat::BasicRGB24),
                                                         shareContext, dc, context);
     if (!glContext->Init()) {
         return nsnull;
     }
 
@@ -592,17 +656,29 @@ CreatePBufferOffscreenContext(const gfxI
                                                 pbattrs.Elements());
     if (!pbuffer) {
         return nsnull;
     }
 
     HDC pbdc = sWGLLibrary.fGetPbufferDC(pbuffer);
     NS_ASSERTION(pbdc, "expected a dc");
 
-    HGLRC context = sWGLLibrary.fCreateContext(pbdc);
+    HGLRC context;
+    if (sWGLLibrary.HasRobustness()) {
+        int attribs[] = {
+            LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+            LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+            NULL
+        };
+
+        context = sWGLLibrary.fCreateContextAttribs(pbdc, nsnull, attribs);
+    } else {
+        context = sWGLLibrary.fCreateContext(pbdc);
+    }
+
     if (!context) {
         sWGLLibrary.fDestroyPbuffer(pbuffer);
         return false;
     }
 
     nsRefPtr<GLContextWGL> glContext = new GLContextWGL(aFormat,
                                                         nsnull,
                                                         pbuffer,
@@ -624,25 +700,38 @@ CreateWindowOffscreenContext(const Conte
     
     HDC dc;
     HWND win = CreateDummyWindow(&dc);
     if (!win) {
         return nsnull;
     }
     
     HGLRC context = sWGLLibrary.fCreateContext(dc);
-    if (!context) {
-        return nsnull;
+    if (sWGLLibrary.HasRobustness()) {
+        int attribs[] = {
+            LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
+            LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
+            NULL
+        };
+
+        context = sWGLLibrary.fCreateContextAttribs(dc, shareContext->Context(), attribs);
+    } else {
+        context = sWGLLibrary.fCreateContext(dc);
+        if (context && shareContext &&
+            !sWGLLibrary.fShareLists(shareContext->Context(), context))
+        {
+            NS_WARNING("wglShareLists failed!");
+
+            sWGLLibrary.fDeleteContext(context);
+            DestroyWindow(win);
+            return nsnull;
+        }
     }
 
-    if (!sWGLLibrary.fShareLists(shareContext->Context(), context)) {
-        NS_WARNING("wglShareLists failed!");
-
-        sWGLLibrary.fDeleteContext(context);
-        DestroyWindow(win);
+    if (!context) {
         return nsnull;
     }
 
     nsRefPtr<GLContextWGL> glContext = new GLContextWGL(aFormat, shareContext,
                                                         dc, context, win, true);
 
     return glContext.forget();
 }
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -3241,9 +3241,10 @@ typedef ptrdiff_t GLintptr;
 #define LOCAL_EGL_LOCK_USAGE_HINT_KHR         0x30C5
 #define LOCAL_EGL_MAP_PRESERVE_PIXELS_KHR     0x30C4
 #define LOCAL_EGL_READ_SURFACE_BIT_KHR        0x0001
 #define LOCAL_EGL_WRITE_SURFACE_BIT_KHR       0x0002
 #define LOCAL_EGL_LOCK_SURFACE_BIT_KHR        0x0080
 #define LOCAL_EGL_CORE_NATIVE_ENGINE          0x305B
 #define LOCAL_EGL_READ                        0x305A
 #define LOCAL_EGL_DRAW                        0x3059
+#define LOCAL_EGL_CONTEXT_LOST                0x300E
 #endif
--- a/gfx/gl/WGLLibrary.h
+++ b/gfx/gl/WGLLibrary.h
@@ -37,17 +37,18 @@
 #include "GLContext.h"
 
 namespace mozilla {
 namespace gl {
 
 class WGLLibrary
 {
 public:
-    WGLLibrary() : mInitialized(false), mOGLLibrary(nsnull) {}
+    WGLLibrary() : mInitialized(false), mOGLLibrary(nsnull),
+    mHasRobustness(false) {}
 
     typedef HGLRC (GLAPIENTRY * PFNWGLCREATECONTEXTPROC) (HDC);
     PFNWGLCREATECONTEXTPROC fCreateContext;
     typedef BOOL (GLAPIENTRY * PFNWGLDELETECONTEXTPROC) (HGLRC);
     PFNWGLDELETECONTEXTPROC fDeleteContext;
     typedef BOOL (GLAPIENTRY * PFNWGLMAKECURRENTPROC) (HDC, HGLRC);
     PFNWGLMAKECURRENTPROC fMakeCurrent;
     typedef PROC (GLAPIENTRY * PFNWGLGETPROCADDRESSPROC) (LPCSTR);
@@ -71,21 +72,30 @@ public:
     typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEPROC) (HANDLE hPbuffer, int iBuffer);
     PFNWGLRELEASETEXIMAGEPROC fReleaseTexImage;
 
     typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATPROC) (HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
     PFNWGLCHOOSEPIXELFORMATPROC fChoosePixelFormat;
     typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, int *piValues);
     PFNWGLGETPIXELFORMATATTRIBIVPROC fGetPixelFormatAttribiv;
 
+    typedef const char * (WINAPI * PFNWGLGETEXTENSIONSSTRINGPROC) (HDC hdc);
+    PFNWGLGETEXTENSIONSSTRINGPROC fGetExtensionsString;
+
+    typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSPROC) (HDC hdc, HGLRC hShareContext, const int *attribList);
+    PFNWGLCREATECONTEXTATTRIBSPROC fCreateContextAttribs;
+
     bool EnsureInitialized();
 
+    bool HasRobustness() const { return mHasRobustness; }
+
 private:
     bool mInitialized;
     PRLibrary *mOGLLibrary;
+    bool mHasRobustness;
 };
 
 // a global WGLLibrary instance
 extern WGLLibrary sWGLLibrary;
 
 } /* namespace gl */
 } /* namespace mozilla */
 
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -995,19 +995,22 @@ LayerManagerOGL::SetupBackBuffer(int aWi
 
   mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackBufferFBO);
   mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                                     LOCAL_GL_COLOR_ATTACHMENT0,
                                     mFBOTextureTarget,
                                     mBackBufferTexture,
                                     0);
 
-  if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) !=
-      LOCAL_GL_FRAMEBUFFER_COMPLETE) {
-    NS_RUNTIMEABORT("Error setting up framebuffer --- framebuffer not complete");
+  GLenum result = mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+  if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+    nsCAutoString msg;
+    msg.Append("Framebuffer not complete -- error 0x");
+    msg.AppendInt(result, 16);
+    NS_RUNTIMEABORT(msg.get());
   }
 
   mBackBufferSize.width = aWidth;
   mBackBufferSize.height = aHeight;
 }
 
 void
 LayerManagerOGL::CopyToTarget()
@@ -1156,19 +1159,22 @@ LayerManagerOGL::CreateFBOWithTexture(co
   mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                                     LOCAL_GL_COLOR_ATTACHMENT0,
                                     mFBOTextureTarget,
                                     tex,
                                     0);
 
   // Making this call to fCheckFramebufferStatus prevents a crash on
   // PowerVR. See bug 695246.
-  if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) !=
-      LOCAL_GL_FRAMEBUFFER_COMPLETE) {
-    NS_RUNTIMEABORT("Error setting up framebuffer --- framebuffer not complete");
+  GLenum result = mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+  if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+    nsCAutoString msg;
+    msg.Append("Framebuffer not complete -- error 0x");
+    msg.AppendInt(result, 16);
+    NS_RUNTIMEABORT(msg.get());
   }
 
   SetupPipeline(aRect.width, aRect.height, DontApplyWorldTransform);
   mGLContext->fScissor(0, 0, aRect.width, aRect.height);
 
   if (aInit == InitModeClear) {
     mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
     mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
--- a/image/test/browser/browser_image.js
+++ b/image/test/browser/browser_image.js
@@ -182,11 +182,12 @@ function nextTest() {
   if (tests.length == 0) {
     finish();
     return;
   }
   tests.shift()();
 }
 
 function test() {
+  ignoreAllUncaughtExceptions();
   nextTest();
 }
 
--- a/js/src/assembler/jit/ExecutableAllocator.cpp
+++ b/js/src/assembler/jit/ExecutableAllocator.cpp
@@ -33,25 +33,25 @@ size_t ExecutableAllocator::pageSize = 0
 size_t ExecutableAllocator::largeAllocSize = 0;
 
 ExecutablePool::~ExecutablePool()
 {
     m_allocator->releasePoolPages(this);
 }
 
 void
-ExecutableAllocator::getCodeStats(size_t& method, size_t& regexp, size_t& unused) const
+ExecutableAllocator::sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const
 {
-    method = 0;
-    regexp = 0;
-    unused = 0;
+    *method = 0;
+    *regexp = 0;
+    *unused = 0;
 
     for (ExecPoolHashSet::Range r = m_pools.all(); !r.empty(); r.popFront()) {
         ExecutablePool* pool = r.front();
-        method += pool->m_mjitCodeMethod;
-        regexp += pool->m_mjitCodeRegexp;
-        unused += pool->m_allocation.size - pool->m_mjitCodeMethod - pool->m_mjitCodeRegexp;
+        *method += pool->m_mjitCodeMethod;
+        *regexp += pool->m_mjitCodeRegexp;
+        *unused += pool->m_allocation.size - pool->m_mjitCodeMethod - pool->m_mjitCodeRegexp;
     }
 }
 
 }
 
 #endif // HAVE(ASSEMBLER)
--- a/js/src/assembler/jit/ExecutableAllocator.h
+++ b/js/src/assembler/jit/ExecutableAllocator.h
@@ -225,17 +225,17 @@ public:
     void releasePoolPages(ExecutablePool *pool) {
         JS_ASSERT(pool->m_allocation.pages);
         if (destroyCallback)
             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
         systemRelease(pool->m_allocation);
         m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
     }
 
-    void getCodeStats(size_t& method, size_t& regexp, size_t& unused) const;
+    void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
 
     void setDestroyCallback(DestroyCallback destroyCallback) {
         this->destroyCallback = destroyCallback;
     }
 
 private:
     static size_t pageSize;
     static size_t largeAllocSize;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4559,16 +4559,28 @@ MOZ_ARG_ENABLE_BOOL(js-diagnostics,
 [  --enable-js-diagnostics
                           Enable JS diagnostic assertions and breakpad data],
     JS_CRASH_DIAGNOSTICS=1,
     JS_CRASH_DIAGNOSTICS= )
 if test -n "$JS_CRASH_DIAGNOSTICS"; then
     AC_DEFINE(JS_CRASH_DIAGNOSTICS)
 fi
 
+dnl ========================================================
+dnl Enable changes that make the shell more deterministic
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(more-deterministic,
+[  --enable-more-deterministic
+                          Enable changes that make the shell more deterministic],
+    JS_MORE_DETERMINISTIC=1,
+    JS_MORE_DETERMINISTIC= )
+if test -n "$JS_MORE_DETERMINISTIC"; then
+    AC_DEFINE(JS_MORE_DETERMINISTIC)
+fi
+
 dnl ======================================================
 dnl = Enable compiling with ccache
 dnl ======================================================
 MOZ_ARG_WITH_STRING(ccache,
 [  --with-ccache[=path/to/ccache]
                           Enable compiling with ccache],
     CCACHE=$withval, CCACHE="no")
 
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -135,21 +135,20 @@ LifoAlloc::freeUnused()
         if (!lastUsed) {
             freeAll();
             return;
         }
         latest = lastUsed;
     }
 
     /* Free all chunks after |latest|. */
-    size_t freed = 0;
-    for (BumpChunk *victim = latest->next(); victim; victim = victim->next()) {
+    for (BumpChunk *victim = latest->next(); victim; victim = victim->next())
         BumpChunk::delete_(victim);
-        freed++;
-    }
+
+    latest->setNext(NULL);
 }
 
 LifoAlloc::BumpChunk *
 LifoAlloc::getOrCreateChunk(size_t n)
 {
     if (first) {
         /* Look for existing, unused BumpChunks to satisfy the request. */
         while (latest->next()) {
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -320,17 +320,18 @@ class LifoAlloc
     JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE)
 
     /* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */
 
     void *allocUnaligned(size_t n);
     void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr);
 };
 
-class LifoAllocScope {
+class LifoAllocScope
+{
     LifoAlloc   *lifoAlloc;
     void        *mark;
     bool        shouldRelease;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit LifoAllocScope(LifoAlloc *lifoAlloc
                             JS_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -121,17 +121,16 @@ BytecodeEmitter::BytecodeEmitter(Parser 
     constList(parser->context),
     upvarIndices(parser->context),
     upvarMap(parser->context),
     globalScope(NULL),
     globalUses(parser->context),
     globalMap(parser->context),
     closedArgs(parser->context),
     closedVars(parser->context),
-    traceIndex(0),
     typesetCount(0)
 {
     flags = TCF_COMPILING;
     memset(&prolog, 0, sizeof prolog);
     memset(&main, 0, sizeof main);
     current = &main;
     firstLine = prolog.currentLine = main.currentLine = lineno;
 }
@@ -1445,31 +1444,28 @@ UpdateLineNumberNotes(JSContext *cx, Byt
     return JS_TRUE;
 }
 
 static ptrdiff_t
 EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
 {
     if (nextpn) {
         /*
-         * Try to give the JSOP_TRACE the same line number as the next
+         * Try to give the JSOP_LOOPHEAD the same line number as the next
          * instruction. nextpn is often a block, in which case the next
          * instruction typically comes from the first statement inside.
          */
         JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
         if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
             nextpn = nextpn->pn_head;
         if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
             return -1;
     }
 
-    uint32 index = bce->traceIndex;
-    if (index < UINT16_MAX)
-        bce->traceIndex++;
-    return Emit3(cx, bce, JSOP_TRACE, UINT16_HI(index), UINT16_LO(index));
+    return Emit1(cx, bce, JSOP_LOOPHEAD);
 }
 
 /*
  * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
  * a type set to store its result.
  */
 static inline void
 CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
@@ -5536,17 +5532,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
     /*
      * Jump down to the loop condition to minimize overhead assuming at
      * least one iteration, as the other loop forms do.
      */
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
-    intN noteIndex2 = NewSrcNote(cx, bce, SRC_TRACE);
+    intN noteIndex2 = NewSrcNote(cx, bce, SRC_LOOPHEAD);
     if (noteIndex2 < 0)
         return false;
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
     if (EmitTraceOp(cx, bce, NULL) < 0)
         return false;
 
@@ -5677,17 +5673,17 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
         jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
         if (jmp < 0)
             return false;
     }
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
 
-    intN noteIndex2 = NewSrcNote(cx, bce, SRC_TRACE);
+    intN noteIndex2 = NewSrcNote(cx, bce, SRC_LOOPHEAD);
     if (noteIndex2 < 0)
         return false;
 
     /* Emit code for the loop body. */
     if (EmitTraceOp(cx, bce, forBody) < 0)
         return false;
     if (!EmitTree(cx, bce, forBody))
         return false;
@@ -6001,17 +5997,17 @@ frontend::EmitTree(JSContext *cx, Byteco
          */
         PushStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top);
         noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
         if (noteIndex < 0)
             return JS_FALSE;
         jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
         if (jmp < 0)
             return JS_FALSE;
-        noteIndex2 = NewSrcNote(cx, bce, SRC_TRACE);
+        noteIndex2 = NewSrcNote(cx, bce, SRC_LOOPHEAD);
         if (noteIndex2 < 0)
             return JS_FALSE;
         top = EmitTraceOp(cx, bce, pn->pn_right);
         if (top < 0)
             return JS_FALSE;
         if (!EmitTree(cx, bce, pn->pn_right))
             return JS_FALSE;
         CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp);
@@ -6032,17 +6028,17 @@ frontend::EmitTree(JSContext *cx, Byteco
         break;
 
       case PNK_DOWHILE:
         /* Emit an annotated nop so we know to decompile a 'do' keyword. */
         noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
         if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
             return JS_FALSE;
 
-        noteIndex2 = NewSrcNote(cx, bce, SRC_TRACE);
+        noteIndex2 = NewSrcNote(cx, bce, SRC_LOOPHEAD);
         if (noteIndex2 < 0)
             return JS_FALSE;
 
         /* Compile the loop body. */
         top = EmitTraceOp(cx, bce, pn->pn_left);
         if (top < 0)
             return JS_FALSE;
         PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -660,17 +660,16 @@ struct BytecodeEmitter : public TreeCont
     GlobalUseVector globalUses;     /* per-script global uses */
     OwnedAtomIndexMapPtr globalMap; /* per-script map of global name to globalUses vector */
 
     /* Vectors of pn_cookie slot values. */
     typedef Vector<uint32, 8> SlotVector;
     SlotVector      closedArgs;
     SlotVector      closedVars;
 
-    uint16          traceIndex;     /* index for the next JSOP_TRACE instruction */
     uint16          typesetCount;   /* Number of JOF_TYPESET opcodes generated */
 
     BytecodeEmitter(Parser *parser, uintN lineno);
     bool init(JSContext *cx, TreeContext::InitBehavior ib = USED_AS_CODE_GENERATOR);
 
     JSContext *context() {
         return parser->context;
     }
@@ -943,17 +942,17 @@ enum SrcNoteType {
     SRC_GENEXP      = 1,        /* JSOP_LAMBDA from generator expression */
     SRC_IF_ELSE     = 2,        /* JSOP_IFEQ bytecode is from an if-then-else */
     SRC_FOR_IN      = 2,        /* JSOP_GOTO to for-in loop condition from
                                    before loop (same arity as SRC_IF_ELSE) */
     SRC_FOR         = 3,        /* JSOP_NOP or JSOP_POP in for(;;) loop head */
     SRC_WHILE       = 4,        /* JSOP_GOTO to for or while loop condition
                                    from before loop, else JSOP_NOP at top of
                                    do-while loop */
-    SRC_TRACE       = 4,        /* For JSOP_TRACE; includes distance to loop end */
+    SRC_LOOPHEAD    = 4,        /* For JSOP_LOOPHEAD; includes distance to loop end */
     SRC_CONTINUE    = 5,        /* JSOP_GOTO is a continue, not a break;
                                    also used on JSOP_ENDINIT if extra comma
                                    at end of array literal: [1,2,,];
                                    JSOP_DUP continuing destructuring pattern */
     SRC_DECL        = 6,        /* type of a declaration (var, const, let*) */
     SRC_DESTRUCT    = 6,        /* JSOP_DUP starting a destructuring assignment
                                    operation, with SRC_DECL_* offset operand */
     SRC_PCDELTA     = 7,        /* distance forward from comma-operator to
--- a/js/src/jit-test/tests/basic/bug657975.js
+++ b/js/src/jit-test/tests/basic/bug657975.js
@@ -15,24 +15,24 @@ x = 0;
 
 // bug 657984 #1
 function f3(){ for(y in x); }
 trap(f3, 5, '')
 f3()
 
 // bug 657984 #2
 function f4(){ for(y in x); }
-trap(f4, 10, '')
+trap(f4, 8, '')
 f4()
 
 // bug 658464
 function f5() {
   for ([, x] in 0) {}
 }
-trap(f5, 9, '')
+trap(f5, 7, '')
 f5()
 
 // bug 658465
 function f6() {
   "use strict";
   print(Math.min(0, 1));
 }
 trap(f6, 10, '')
@@ -55,25 +55,25 @@ for (a in f8())
   (function() {})()
 
 // bug 659043
 f9 = (function() {
   for (let a = 0; a < 0; ++a) {
     for each(let w in []) {}
   }
 })
-trap(f9, 27, undefined);
+trap(f9, 22, undefined);
 for (b in f9())
   (function() {})()
 
 // bug 659233
 f10 = (function() {
     while (h) {
         continue
     }
 })
 trap(f10, 0, '');
 try { f10() } catch (e) {}
 
 // bug 659337
 f11 = Function("for (x = 0; x < 6; x++) { gc() }");
-trap(f11, 25, '');
+trap(f11, 23, '');
 f11()
rename from js/src/jit-test/tests/basic/bug685321.js
rename to js/src/jit-test/tests/basic/bug685321-1.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug685321-2.js
@@ -0,0 +1,13 @@
+var o = {};
+function f() {
+    function g() {
+        x = 80;
+        return x;
+    };
+    Object.defineProperty(o, "f", {get:g});
+    var [x] = 0;
+    x = {};
+    2 + o.f;
+    print(x);
+}
+f();
--- a/js/src/jit-test/tests/basic/testBug683470.js
+++ b/js/src/jit-test/tests/basic/testBug683470.js
@@ -6,10 +6,10 @@ f = (function() {
     Object.defineProperty(this, "x", ({}));
   }
   for each(let d in [0, 0]) {
     try {
       b(d);
     } catch (e) {}
   }
 })
-trap(f, 54, undefined);
+trap(f, 52, undefined);
 f()
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -88,19 +88,16 @@ ScriptAnalysis::addJump(JSContext *cx, u
     JS_ASSERT(code->stackDepth == stackDepth);
 
     code->jumpTarget = true;
 
     if (offset < *currentOffset) {
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
 
-        if (JSOp(*pc) == JSOP_TRACE || JSOp(*pc) == JSOP_NOTRACE)
-            code->loopHead = true;
-
         /* Scripts containing loops are never inlined. */
         isInlineable = false;
 
         /* Don't follow back edges to bytecode which has already been analyzed. */
         if (!code->analyzed) {
             if (*forwardJump == 0)
                 *forwardJump = *currentOffset;
             *currentOffset = offset;
@@ -665,17 +662,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
         if (loop && code->safePoint)
             loop->hasSafePoints = true;
 
         jsbytecode *pc = script->code + offset;
         UntrapOpcode untrap(cx, script, pc);
 
         JSOp op = (JSOp) *pc;
 
-        if ((op == JSOP_TRACE || op == JSOP_NOTRACE) && code->loop) {
+        if (op == JSOP_LOOPHEAD && code->loop) {
             /*
              * This is the head of a loop, we need to go and make sure that any
              * variables live at the head are live at the backedge and points prior.
              * For each such variable, look for the last lifetime segment in the body
              * and extend it to the end of the loop.
              */
             JS_ASSERT(loop == code->loop);
             unsigned backedge = code->loop->backedge;
@@ -812,17 +809,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
             if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock)
                 loop->lastBlock = loop->entry;
 
             if (targetOffset < offset) {
                 /* This is a loop back edge, no lifetime to pull in yet. */
 
 #ifdef DEBUG
                 JSOp nop = JSOp(script->code[targetOffset]);
-                JS_ASSERT(nop == JSOP_TRACE || nop == JSOP_NOTRACE || nop == JSOP_TRAP);
+                JS_ASSERT(nop == JSOP_LOOPHEAD || nop == JSOP_TRAP);
 #endif
 
                 /*
                  * If we already have a loop, it is an outer loop and we
                  * need to prune the last block in the loop --- we do not
                  * track 'continue' statements for outer loops.
                  */
                 if (loop && loop->entry > loop->lastBlock)
@@ -1169,17 +1166,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
             offset = successorOffset;
             continue;
         }
 
         if (code->stackDepth > stackDepth)
             PodZero(stack + stackDepth, code->stackDepth - stackDepth);
         stackDepth = code->stackDepth;
 
-        if ((op == JSOP_TRACE || op == JSOP_NOTRACE) && code->loop) {
+        if (op == JSOP_LOOPHEAD && code->loop) {
             /*
              * Make sure there is a pending value array for phi nodes at the
              * loop head. We won't be able to clear these until we reach the
              * loop's back edge.
              *
              * We need phi nodes for all variables which might be modified
              * during the loop. This ensures that in the loop body we have
              * already updated state to reflect possible changes that happen
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -93,19 +93,16 @@ class Bytecode
   public:
     Bytecode() { PodZero(this); }
 
     /* --------- Bytecode analysis --------- */
 
     /* Whether there are any incoming jumps to this instruction. */
     bool jumpTarget : 1;
 
-    /* There is a backwards jump to this instruction. */
-    bool loopHead : 1;
-
     /* Whether there is fallthrough to this instruction from a non-branching instruction. */
     bool fallthrough : 1;
 
     /* Whether this instruction is the fall through point of a conditional jump. */
     bool jumpFallthrough : 1;
 
     /* Whether this instruction can be branched to from a switch statement. Implies jumpTarget. */
     bool switchTarget : 1;
--- a/js/src/jsapi-tests/testFuncCallback.cpp
+++ b/js/src/jsapi-tests/testFuncCallback.cpp
@@ -15,18 +15,17 @@ static void
 funcTransition(const JSFunction *,
                const JSScript *,
                const JSContext *cx,
                int entering)
 {
     if (entering > 0) {
         ++depth;
         ++enters;
-        if (! JS_ON_TRACE(cx))
-            ++interpreted;
+        ++interpreted;
     } else {
         --depth;
         ++leaves;
     }
 }
 
 static JSBool called2 = false;
 
@@ -88,22 +87,23 @@ BEGIN_TEST(testFuncCallback_bug507012)
     JS_SetFunctionCallback(cx, funcTransition);
     enters = leaves = depth = 0;
     EXEC("f(3)");
     CHECK_EQUAL(enters, 1+3);
     CHECK_EQUAL(leaves, 1+3);
     CHECK_EQUAL(depth, 0);
     interpreted = enters = leaves = depth = 0;
 
-    // Check calls invoked while running on trace
+    // Check calls invoked while running on trace -- or now, perhaps on
+    // IonMonkey's equivalent, if it ever starts to exist?
     EXEC("function g () { ++x; }");
     interpreted = enters = leaves = depth = 0;
-    EXEC("for (i = 0; i < 50; ++i) { g(); }");
-    CHECK_EQUAL(enters, 1+50);
-    CHECK_EQUAL(leaves, 1+50);
+    EXEC("for (i = 0; i < 5000; ++i) { g(); }");
+    CHECK_EQUAL(enters, 1+5000);
+    CHECK_EQUAL(leaves, 1+5000);
     CHECK_EQUAL(depth, 0);
 
     // Test nesting callbacks via JS_GetFunctionCallback()
     JS_SetFunctionCallback(cx, funcTransition);
     innerCallback = JS_GetFunctionCallback(cx);
     JS_SetFunctionCallback(cx, funcTransitionOverlay);
 
     EXEC("x = 0; function f (n) { if (n > 1) { f(n - 1); } }");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1136,16 +1136,22 @@ JS_GetRuntime(JSContext *cx)
 }
 
 JS_PUBLIC_API(JSContext *)
 JS_ContextIterator(JSRuntime *rt, JSContext **iterp)
 {
     return js_ContextIterator(rt, JS_TRUE, iterp);
 }
 
+JS_PUBLIC_API(JSContext *)
+JS_ContextIteratorUnlocked(JSRuntime *rt, JSContext **iterp)
+{
+    return js_ContextIterator(rt, JS_FALSE, iterp);
+}
+
 JS_PUBLIC_API(JSVersion)
 JS_GetVersion(JSContext *cx)
 {
     return VersionNumber(cx->findVersion());
 }
 
 JS_PUBLIC_API(JSVersion)
 JS_SetVersion(JSContext *cx, JSVersion newVersion)
@@ -5292,16 +5298,30 @@ JS_SaveFrameChain(JSContext *cx)
 
 JS_PUBLIC_API(void)
 JS_RestoreFrameChain(JSContext *cx)
 {
     CHECK_REQUEST(cx);
     cx->stack.restoreFrameChain();
 }
 
+#ifdef MOZ_TRACE_JSCALLS
+JS_PUBLIC_API(void)
+JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
+{
+    cx->functionCallback = fcb;
+}
+
+JS_PUBLIC_API(JSFunctionCallback)
+JS_GetFunctionCallback(JSContext *cx)
+{
+    return cx->functionCallback;
+}
+#endif
+
 /************************************************************************/
 JS_PUBLIC_API(JSString *)
 JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
 {
     CHECK_REQUEST(cx);
     return js_NewStringCopyN(cx, s, n);
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1113,16 +1113,24 @@ typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
 typedef JSBool
 (* JSOperationCallback)(JSContext *cx);
 
 typedef void
 (* JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report);
 
+#ifdef MOZ_TRACE_JSCALLS
+typedef void
+(* JSFunctionCallback)(const JSFunction *fun,
+                       const JSScript *scr,
+                       const JSContext *cx,
+                       int entering);
+#endif
+
 /*
  * Possible exception types. These types are part of a JSErrorFormatString
  * structure. They define which error to throw in case of a runtime error.
  * JSEXN_NONE marks an unthrowable error.
  */
 typedef enum JSExnType {
     JSEXN_NONE = -1,
       JSEXN_ERR,
@@ -2096,16 +2104,19 @@ extern JS_PUBLIC_API(void)
 JS_SetContextPrivate(JSContext *cx, void *data);
 
 extern JS_PUBLIC_API(JSRuntime *)
 JS_GetRuntime(JSContext *cx);
 
 extern JS_PUBLIC_API(JSContext *)
 JS_ContextIterator(JSRuntime *rt, JSContext **iterp);
 
+extern JS_PUBLIC_API(JSContext *)
+JS_ContextIteratorUnlocked(JSRuntime *rt, JSContext **iterp);
+
 extern JS_PUBLIC_API(JSVersion)
 JS_GetVersion(JSContext *cx);
 
 extern JS_PUBLIC_API(JSVersion)
 JS_SetVersion(JSContext *cx, JSVersion version);
 
 extern JS_PUBLIC_API(const char *)
 JS_VersionToString(JSVersion version);
@@ -4172,16 +4183,33 @@ JS_IsRunning(JSContext *cx);
  * JS_SaveFrameChain deals with cx not having any code running on it.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_SaveFrameChain(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_RestoreFrameChain(JSContext *cx);
 
+#ifdef MOZ_TRACE_JSCALLS
+/*
+ * The callback is expected to be quick and noninvasive. It should not
+ * trigger interrupts, turn on debugging, or produce uncaught JS
+ * exceptions. The state of the stack and registers in the context
+ * cannot be relied upon, since this callback may be invoked directly
+ * from either JIT. The 'entering' field means we are entering a
+ * function if it is positive, leaving a function if it is zero or
+ * negative.
+ */
+extern JS_PUBLIC_API(void)
+JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);
+
+extern JS_PUBLIC_API(JSFunctionCallback)
+JS_GetFunctionCallback(JSContext *cx);
+#endif /* MOZ_TRACE_JSCALLS */
+
 /************************************************************************/
 
 /*
  * Strings.
  *
  * NB: JS_NewUCString takes ownership of bytes on success, avoiding a copy;
  * but on error (signified by null return), it leaves chars owned by the
  * caller. So the caller must free bytes in the error case, if it has no use
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -132,16 +132,45 @@ ThreadData::~ThreadData()
 
 bool
 ThreadData::init()
 {
     JS_ASSERT(!repCache);
     return stackSpace.init() && !!(dtoaState = js_NewDtoaState());
 }
 
+#ifdef JS_THREADSAFE
+void
+ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                                size_t *regexpCode, size_t *stackCommitted)
+{
+    /*
+     * There are other ThreadData members that could be measured; the ones
+     * below have been seen by DMD to be worth measuring.  More stuff may be
+     * added later.
+     */
+
+    /*
+     * The computedSize is 0 because sizeof(DtoaState) isn't available here and
+     * it's not worth making it available.
+     */
+    *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0);
+
+    *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf);
+
+    size_t method = 0, regexp = 0, unused = 0;
+    if (execAlloc)
+        execAlloc->sizeOfCode(&method, &regexp, &unused);
+    JS_ASSERT(method == 0);     /* this execAlloc is only used for regexp code */
+    *regexpCode = regexp + unused;
+
+    *stackCommitted = stackSpace.sizeOfCommitted();
+}
+#endif
+
 void
 ThreadData::triggerOperationCallback(JSRuntime *rt)
 {
     JS_ASSERT(rt == this->rt);
 
     /*
      * Use JS_ATOMIC_SET and JS_ATOMIC_INCREMENT in the hope that it ensures
      * the write will become immediately visible to other processors polling
@@ -214,16 +243,24 @@ JSScript *
 js_GetCurrentScript(JSContext *cx)
 {
     return cx->hasfp() ? cx->fp()->maybeScript() : NULL;
 }
 
 
 #ifdef JS_THREADSAFE
 
+void
+JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                              size_t *regexpCode, size_t *stackCommitted)
+{
+    data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted);
+    *normal += mallocSizeOf(this, sizeof(JSThread));
+}
+
 JSThread *
 js_CurrentThreadAndLockGC(JSRuntime *rt)
 {
     void *id = js_CurrentThreadId();
     JS_LOCK_GC(rt);
 
     /*
      * We must not race with a GC that accesses cx->thread for JSContext
@@ -770,16 +807,27 @@ js_ReportOutOfMemory(JSContext *cx)
         AutoAtomicIncrement incr(&cx->runtime->inOOMReport);
         onError(cx, msg, &report);
     }
 }
 
 JS_FRIEND_API(void)
 js_ReportOverRecursed(JSContext *maybecx)
 {
+#ifdef JS_MORE_DETERMINISTIC
+    /*
+     * We cannot make stack depth deterministic across different
+     * implementations (e.g. JIT vs. interpreter will differ in
+     * their maximum stack depth).
+     * However, we can detect externally when we hit the maximum
+     * stack depth which is useful for external testing programs
+     * like fuzzers.
+     */
+    fprintf(stderr, "js_ReportOverRecursed called\n");
+#endif
     if (maybecx)
         JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 }
 
 void
 js_ReportAllocationOverflow(JSContext *maybecx)
 {
     if (maybecx)
@@ -1650,16 +1698,28 @@ JSContext::updateJITEnabled()
 # if defined JS_CPU_X86 || defined JS_CPU_X64
                        && JSC::MacroAssemblerX86Common::getSSEState() >=
                           JSC::MacroAssemblerX86Common::HasSSE2
 # endif
                         ;
 #endif
 }
 
+size_t
+JSContext::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const
+{
+    /*
+     * There are other JSContext members that could be measured; the following
+     * ones have been found by DMD to be worth measuring.  More stuff may be
+     * added later.
+     */
+    return mallocSizeOf(this, sizeof(JSContext)) +
+           busyArrays.sizeOfExcludingThis(mallocSizeOf);
+}
+
 namespace js {
 
 AutoEnumStateRooter::~AutoEnumStateRooter()
 {
     if (!stateValue.isNull()) {
         DebugOnly<JSBool> ok =
             obj->enumerate(context, JSENUMERATE_DESTROY, &stateValue, 0);
         JS_ASSERT(ok);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -222,16 +222,21 @@ struct ThreadData {
     void purge(JSContext *cx) {
         tempLifoAlloc.freeUnused();
         gsnCache.purge();
 
         /* FIXME: bug 506341. */
         propertyCache.purge(cx);
     }
 
+#ifdef JS_THREADSAFE
+    void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, size_t *temporary,
+                             size_t *regexpCode, size_t *stackCommitted);
+#endif
+
     /* This must be called with the GC lock held. */
     void triggerOperationCallback(JSRuntime *rt);
 
     /*
      * Frames currently running in js::Interpret. See InterpreterFrames for
      * details.
      */
     InterpreterFrames *interpreterFrames;
@@ -252,17 +257,17 @@ struct JSThread {
                         js::SystemAllocPolicy> Map;
 
     /* Linked list of all contexts in use on this thread. */
     JSCList             contextList;
 
     /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */
     void                *id;
 
-    /* Number of JS_SuspendRequest calls withot JS_ResumeRequest. */
+    /* Number of JS_SuspendRequest calls without JS_ResumeRequest. */
     unsigned            suspendCount;
 
 # ifdef DEBUG
     unsigned            checkRequestDepth;
 # endif
 
     /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
     js::ThreadData      data;
@@ -281,16 +286,20 @@ struct JSThread {
     ~JSThread() {
         /* The thread must have zero contexts. */
         JS_ASSERT(JS_CLIST_IS_EMPTY(&contextList));
     }
 
     bool init() {
         return data.init();
     }
+
+    JS_FRIEND_API(void) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal,
+                                            size_t *temporary, size_t *regexpCode,
+                                            size_t *stackCommitted);
 };
 
 #define JS_THREAD_DATA(cx)      (&(cx)->thread()->data)
 
 extern JSThread *
 js_CurrentThreadAndLockGC(JSRuntime *rt);
 
 /*
@@ -1234,16 +1243,18 @@ struct JSContext
 #endif
 
     /*
      * See JS_SetTrustedPrincipals in jsapi.h.
      * Note: !cx->compartment is treated as trusted.
      */
     bool runningWithTrustedPrincipals() const;
 
+    JS_FRIEND_API(size_t) sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const;
+
     static inline JSContext *fromLinkField(JSCList *link) {
         JS_ASSERT(link);
         return reinterpret_cast<JSContext *>(uintptr_t(link) - offsetof(JSContext, link));
     }
 
 #ifdef JS_THREADSAFE
     static inline JSContext *fromThreadLinks(JSCList *link) {
         JS_ASSERT(link);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -44,17 +44,16 @@
 #include "jsgcmark.h"
 #include "jsiter.h"
 #include "jsmath.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 #include "assembler/wtf/Platform.h"
-#include "assembler/jit/ExecutableAllocator.h"
 #include "yarr/BumpPointerAllocator.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/PolyIC.h"
 #include "methodjit/MonoIC.h"
 #include "vm/Debugger.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
@@ -140,24 +139,24 @@ JSCompartment::ensureJaegerCompartmentEx
         cx->delete_(jc);
         return false;
     }
     jaegerCompartment_ = jc;
     return true;
 }
 
 void
-JSCompartment::getMjitCodeStats(size_t& method, size_t& regexp, size_t& unused) const
+JSCompartment::sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const
 {
     if (jaegerCompartment_) {
-        jaegerCompartment_->execAlloc()->getCodeStats(method, regexp, unused);
+        jaegerCompartment_->execAlloc()->sizeOfCode(method, regexp, unused);
     } else {
-        method = 0;
-        regexp = 0;
-        unused = 0;
+        *method = 0;
+        *regexp = 0;
+        *unused = 0;
     }
 }
 #endif
 
 bool
 JSCompartment::wrap(JSContext *cx, Value *vp)
 {
     JS_ASSERT(cx->compartment == this);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -228,17 +228,17 @@ struct JS_FRIEND_API(JSCompartment) {
 
     js::mjit::JaegerCompartment *jaegerCompartment() const {
         JS_ASSERT(jaegerCompartment_);
         return jaegerCompartment_;
     }
 
     bool ensureJaegerCompartmentExists(JSContext *cx);
 
-    void getMjitCodeStats(size_t& method, size_t& regexp, size_t& unused) const;
+    void sizeOfCode(size_t *method, size_t *regexp, size_t *unused) const;
 #endif
 
     /*
      * Shared scope property tree, and arena-pool for allocating its nodes.
      */
     js::PropertyTree             propertyTree;
 
 #ifdef DEBUG
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1606,32 +1606,16 @@ bool
 js_ResumeVtune()
 {
     VTResume();
     return true;
 }
 
 #endif /* MOZ_VTUNE */
 
-#ifdef MOZ_TRACE_JSCALLS
-
-JS_PUBLIC_API(void)
-JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb)
-{
-    cx->functionCallback = fcb;
-}
-
-JS_PUBLIC_API(JSFunctionCallback)
-JS_GetFunctionCallback(JSContext *cx)
-{
-    return cx->functionCallback;
-}
-
-#endif /* MOZ_TRACE_JSCALLS */
-
 JS_PUBLIC_API(void)
 JS_DumpBytecode(JSContext *cx, JSScript *script)
 {
 #if defined(DEBUG)
     LifoAlloc lifoAlloc(1024);
     Sprinter sprinter;
     INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
 
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -573,38 +573,16 @@ js_StopVtune();
 extern JS_FRIEND_API(bool)
 js_PauseVtune();
 
 extern JS_FRIEND_API(bool)
 js_ResumeVtune();
 
 #endif /* MOZ_VTUNE */
 
-#ifdef MOZ_TRACE_JSCALLS
-typedef void (*JSFunctionCallback)(const JSFunction *fun,
-                                   const JSScript *scr,
-                                   const JSContext *cx,
-                                   int entering);
-
-/*
- * The callback is expected to be quick and noninvasive. It should not
- * trigger interrupts, turn on debugging, or produce uncaught JS
- * exceptions. The state of the stack and registers in the context
- * cannot be relied upon, since this callback may be invoked directly
- * from either JIT. The 'entering' field means we are entering a
- * function if it is positive, leaving a function if it is zero or
- * negative.
- */
-extern JS_PUBLIC_API(void)
-JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);
-
-extern JS_PUBLIC_API(JSFunctionCallback)
-JS_GetFunctionCallback(JSContext *cx);
-#endif /* MOZ_TRACE_JSCALLS */
-
 extern JS_PUBLIC_API(void)
 JS_DumpBytecode(JSContext *cx, JSScript *script);
 
 extern JS_PUBLIC_API(void)
 JS_DumpCompartmentBytecode(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_DumpPCCounts(JSContext *cx, JSScript *script);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3378,18 +3378,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
     }
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
       case JSOP_NOP:
-      case JSOP_TRACE:
-      case JSOP_NOTRACE:
+      case JSOP_LOOPHEAD:
       case JSOP_GOTO:
       case JSOP_GOTOX:
       case JSOP_IFEQ:
       case JSOP_IFEQX:
       case JSOP_IFNE:
       case JSOP_IFNEX:
       case JSOP_LINENO:
       case JSOP_DEFCONST:
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1903,33 +1903,29 @@ ADD_EMPTY_CASE(JSOP_NOP)
 ADD_EMPTY_CASE(JSOP_UNUSED0)
 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
 ADD_EMPTY_CASE(JSOP_TRY)
 #if JS_HAS_XML_SUPPORT
 ADD_EMPTY_CASE(JSOP_STARTXML)
 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 #endif
 ADD_EMPTY_CASE(JSOP_NULLBLOCKCHAIN)
+ADD_EMPTY_CASE(JSOP_LOOPHEAD)
 END_EMPTY_CASES
 
-BEGIN_CASE(JSOP_TRACE)
-BEGIN_CASE(JSOP_NOTRACE)
-    /* No-op */
-END_CASE(JSOP_TRACE)
-
 BEGIN_CASE(JSOP_LABEL)
 END_CASE(JSOP_LABEL)
 
 BEGIN_CASE(JSOP_LABELX)
 END_CASE(JSOP_LABELX)
 
 check_backedge:
 {
     CHECK_BRANCH();
-    if (op != JSOP_NOTRACE && op != JSOP_TRACE)
+    if (op != JSOP_LOOPHEAD)
         DO_OP();
 
 #ifdef JS_METHODJIT
     if (!useMethodJIT)
         DO_OP();
     mjit::CompileStatus status =
         mjit::CanMethodJITAtBranch(cx, script, regs.fp(), regs.pc);
     if (status == mjit::Compile_Error)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -4231,17 +4231,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * DecompileExpression or recursively from case
                      * JSOP_{NOP,AND,OR}.)
                      *
                      * There's no special case for |if (genexp)| because the
                      * compiler optimizes that to |if (true)|.
                      */
                     pc2 = pc + len;
                     op = JSOp(*pc2);
-                    if (op == JSOP_TRACE || op == JSOP_NOP)
+                    if (op == JSOP_LOOPHEAD || op == JSOP_NOP)
                         pc2 += JSOP_NOP_LENGTH;
                     LOCAL_ASSERT(pc2 < endpc ||
                                  endpc < outer->code + outer->length);
                     LOCAL_ASSERT(ss2.top == 1);
                     ss2.opcodes[0] = JSOP_POP;
                     if (pc2 == endpc) {
                         op = JSOP_SETNAME;
                     } else {
@@ -5476,33 +5476,27 @@ ReconstructPCStack(JSContext *cx, JSScri
 namespace js {
 
 bool
 CallResultEscapes(jsbytecode *pc)
 {
     /*
      * If we see any of these sequences, the result is unused:
      * - call / pop
-     * - call / trace / pop
      *
      * If we see any of these sequences, the result is only tested for nullness:
      * - call / ifeq
-     * - call / trace / ifeq
      * - call / not / ifeq
-     * - call / trace / not / ifeq
      */
 
     if (*pc != JSOP_CALL)
         return true;
 
     pc += JSOP_CALL_LENGTH;
 
-    if (*pc == JSOP_TRACE)
-        pc += JSOP_TRACE_LENGTH;
-
     if (*pc == JSOP_POP)
         return false;
 
     if (*pc == JSOP_NOT)
         pc += JSOP_NOT_LENGTH;
 
     return (*pc != JSOP_IFEQ);
 }
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -276,18 +276,18 @@ OPDEF(JSOP_UNUSED0,   105,"unused0",    
 
 /* The argument is the offset to the next statement and is used by IonMonkey. */
 OPDEF(JSOP_LABEL,     106,"label",     NULL,          3,  0,  0,  0,  JOF_JUMP)
 OPDEF(JSOP_LABELX,    107,"labelx",    NULL,          5,  0,  0,  0,  JOF_JUMPX)
 
 /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
 OPDEF(JSOP_FUNCALL,   108,"funcall",    NULL,         3, -1,  1, 18,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
 
-/* This opcode stores an index that is unique to the given loop. */
-OPDEF(JSOP_TRACE,     109,"trace",      NULL,         3,  0,  0,  0,  JOF_UINT16)
+/* This opcode is the target of the backwards jump for some loop. */
+OPDEF(JSOP_LOOPHEAD,  109,"loophead",   NULL,         1,  0,  0,  0,  JOF_BYTE)
 
 /* ECMA-compliant assignment ops. */
 OPDEF(JSOP_BINDNAME,  110,"bindname",   NULL,         3,  0,  1,  0,  JOF_ATOM|JOF_NAME|JOF_SET)
 OPDEF(JSOP_SETNAME,   111,"setname",    NULL,         3,  2,  1,  3,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
 
 /* Exception handling ops. */
 OPDEF(JSOP_THROW,     112,js_throw_str, NULL,         1,  1,  0,  0,  JOF_BYTE)
 
@@ -429,158 +429,157 @@ OPDEF(JSOP_SETXMLNAME,    171,"setxmlnam
 OPDEF(JSOP_XMLNAME,       172,"xmlname",    NULL,     1,  1,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_DESCENDANTS,   173,"descendants",NULL,     1,  2,  1, 18,  JOF_BYTE)
 OPDEF(JSOP_FILTER,        174,"filter",     NULL,     3,  1,  1,  0,  JOF_JUMP)
 OPDEF(JSOP_ENDFILTER,     175,"endfilter",  NULL,     3,  2,  1, 18,  JOF_JUMP)
 OPDEF(JSOP_TOXML,         176,"toxml",      NULL,     1,  1,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_TOXMLLIST,     177,"toxmllist",  NULL,     1,  1,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_XMLTAGEXPR,    178,"xmltagexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_XMLELTEXPR,    179,"xmleltexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
-OPDEF(JSOP_NOTRACE,       180,"notrace",    NULL,     3,  0,  0,  0,  JOF_UINT16)
-OPDEF(JSOP_XMLCDATA,      181,"xmlcdata",   NULL,     3,  0,  1, 19,  JOF_ATOM)
-OPDEF(JSOP_XMLCOMMENT,    182,"xmlcomment", NULL,     3,  0,  1, 19,  JOF_ATOM)
-OPDEF(JSOP_XMLPI,         183,"xmlpi",      NULL,     3,  1,  1, 19,  JOF_ATOM)
-OPDEF(JSOP_DELDESC,       184,"deldesc",    NULL,     1,  2,  1, 15,  JOF_BYTE|JOF_ELEM|JOF_DEL)
+OPDEF(JSOP_XMLCDATA,      180,"xmlcdata",   NULL,     3,  0,  1, 19,  JOF_ATOM)
+OPDEF(JSOP_XMLCOMMENT,    181,"xmlcomment", NULL,     3,  0,  1, 19,  JOF_ATOM)
+OPDEF(JSOP_XMLPI,         182,"xmlpi",      NULL,     3,  1,  1, 19,  JOF_ATOM)
+OPDEF(JSOP_DELDESC,       183,"deldesc",    NULL,     1,  2,  1, 15,  JOF_BYTE|JOF_ELEM|JOF_DEL)
 
-OPDEF(JSOP_CALLPROP,      185,"callprop",   NULL,     3,  1,  2, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3)
+OPDEF(JSOP_CALLPROP,      184,"callprop",   NULL,     3,  1,  2, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3)
 
 /*
  * These opcodes contain a reference to the current blockChain object.
  * They are emitted directly after instructions, such as DEFFUN, that need fast access to
  * the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
  * does not permit NULL object references, since it stores an index into a table of
  * objects.
  */
-OPDEF(JSOP_BLOCKCHAIN,    186,"blockchain",    NULL,  3,  0,  0,  0, JOF_OBJECT)
-OPDEF(JSOP_NULLBLOCKCHAIN,187,"nullblockchain",NULL,  1,  0,  0,  0, JOF_BYTE)
+OPDEF(JSOP_BLOCKCHAIN,    185,"blockchain",    NULL,  3,  0,  0,  0, JOF_OBJECT)
+OPDEF(JSOP_NULLBLOCKCHAIN,186,"nullblockchain",NULL,  1,  0,  0,  0, JOF_BYTE)
 
 /*
  * Opcode to hold 24-bit immediate integer operands.
  */
-OPDEF(JSOP_UINT24,        188,"uint24",     NULL,     4,  0,  1, 16,  JOF_UINT24)
+OPDEF(JSOP_UINT24,        187,"uint24",     NULL,     4,  0,  1, 16,  JOF_UINT24)
 
 /*
  * Opcodes to allow 24-bit atom or object indexes. Whenever an index exceeds
  * the 16-bit limit, the index-accessing bytecode must be bracketed by
  * JSOP_INDEXBASE and JSOP_RESETBASE to provide the upper bits of the index.
  * See jsemit.c, EmitIndexOp.
  */
-OPDEF(JSOP_INDEXBASE,     189,"indexbase",  NULL,     2,  0,  0,  0,  JOF_UINT8|JOF_INDEXBASE)
-OPDEF(JSOP_RESETBASE,     190,"resetbase",  NULL,     1,  0,  0,  0,  JOF_BYTE)
-OPDEF(JSOP_RESETBASE0,    191,"resetbase0", NULL,     1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_INDEXBASE,     188,"indexbase",  NULL,     2,  0,  0,  0,  JOF_UINT8|JOF_INDEXBASE)
+OPDEF(JSOP_RESETBASE,     189,"resetbase",  NULL,     1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_RESETBASE0,    190,"resetbase0", NULL,     1,  0,  0,  0,  JOF_BYTE)
 
 /*
  * Opcodes to help the decompiler deal with XML.
  */
-OPDEF(JSOP_STARTXML,      192,"startxml",    NULL,    1,  0,  0,  0,  JOF_BYTE)
-OPDEF(JSOP_STARTXMLEXPR,  193,"startxmlexpr",NULL,    1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_STARTXML,      191,"startxml",    NULL,    1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_STARTXMLEXPR,  192,"startxmlexpr",NULL,    1,  0,  0,  0,  JOF_BYTE)
 
-OPDEF(JSOP_CALLELEM,      194, "callelem",   NULL,    1,  2,  2, 18,  JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC|JOF_CALLOP)
+OPDEF(JSOP_CALLELEM,      193, "callelem",   NULL,    1,  2,  2, 18,  JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC|JOF_CALLOP)
 
 /*
  * Stop interpretation, emitted at end of script to save the threaded bytecode
  * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c).
  */
-OPDEF(JSOP_STOP,          195,"stop",        NULL,    1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_STOP,          194,"stop",        NULL,    1,  0,  0,  0,  JOF_BYTE)
 
 /*
  * Get an extant property value, throwing ReferenceError if the identified
  * property does not exist.
  */
-OPDEF(JSOP_GETXPROP,      196,"getxprop",    NULL,    3,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET)
+OPDEF(JSOP_GETXPROP,      195,"getxprop",    NULL,    3,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET)
 
-OPDEF(JSOP_CALLXMLNAME,   197, "callxmlname",  NULL,  1,  1,  2, 19,  JOF_BYTE|JOF_CALLOP)
+OPDEF(JSOP_CALLXMLNAME,   196, "callxmlname",  NULL,  1,  1,  2, 19,  JOF_BYTE|JOF_CALLOP)
 
 /*
  * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef).
  */
-OPDEF(JSOP_TYPEOFEXPR,    198,"typeofexpr",  NULL,    1,  1,  1, 15,  JOF_BYTE|JOF_DETECTING)
+OPDEF(JSOP_TYPEOFEXPR,    197,"typeofexpr",  NULL,    1,  1,  1, 15,  JOF_BYTE|JOF_DETECTING)
 
 /*
  * Block-local scope support.
  */
-OPDEF(JSOP_ENTERBLOCK,    199,"enterblock",  NULL,    3,  0, -1,  0,  JOF_OBJECT)
-OPDEF(JSOP_LEAVEBLOCK,    200,"leaveblock",  NULL,    5, -1,  0,  0,  JOF_UINT16)
+OPDEF(JSOP_ENTERBLOCK,    198,"enterblock",  NULL,    3,  0, -1,  0,  JOF_OBJECT)
+OPDEF(JSOP_LEAVEBLOCK,    199,"leaveblock",  NULL,    5, -1,  0,  0,  JOF_UINT16)
 
 /* Jump to target if top of stack value isn't callable. */
-OPDEF(JSOP_IFCANTCALLTOP, 201,"ifcantcalltop",NULL,   3,  1,  1,  0,  JOF_JUMP|JOF_DETECTING)
+OPDEF(JSOP_IFCANTCALLTOP, 200,"ifcantcalltop",NULL,   3,  1,  1,  0,  JOF_JUMP|JOF_DETECTING)
 
 /* Throws a TypeError if the value at the top of the stack is not primitive. */
-OPDEF(JSOP_PRIMTOP,       202,"primtop",     NULL,    2,  1,  1,  0,  JOF_INT8)
+OPDEF(JSOP_PRIMTOP,       201,"primtop",     NULL,    2,  1,  1,  0,  JOF_INT8)
 
 /*
  * Generator and array comprehension support.
  */
-OPDEF(JSOP_GENERATOR,     203,"generator",   NULL,    1,  0,  0,  0,  JOF_BYTE)
-OPDEF(JSOP_YIELD,         204,"yield",       NULL,    1,  1,  1,  1,  JOF_BYTE)
-OPDEF(JSOP_ARRAYPUSH,     205,"arraypush",   NULL,    3,  1,  0,  3,  JOF_LOCAL)
+OPDEF(JSOP_GENERATOR,     202,"generator",   NULL,    1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_YIELD,         203,"yield",       NULL,    1,  1,  1,  1,  JOF_BYTE)
+OPDEF(JSOP_ARRAYPUSH,     204,"arraypush",   NULL,    3,  1,  0,  3,  JOF_LOCAL)
 
 /*
  * Get the built-in function::foo namespace and push it.
  */
-OPDEF(JSOP_GETFUNNS,      206,"getfunns",   NULL,     1,  0,  1, 19,  JOF_BYTE)
+OPDEF(JSOP_GETFUNNS,      205,"getfunns",   NULL,     1,  0,  1, 19,  JOF_BYTE)
 
 /*
  * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
  */
-OPDEF(JSOP_ENUMCONSTELEM, 207,"enumconstelem",NULL,   1,  3,  0,  3,  JOF_BYTE|JOF_SET)
+OPDEF(JSOP_ENUMCONSTELEM, 206,"enumconstelem",NULL,   1,  3,  0,  3,  JOF_BYTE|JOF_SET)
 
 /*
  * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
  * which must be moved down when the block pops.
  */
-OPDEF(JSOP_LEAVEBLOCKEXPR,208,"leaveblockexpr",NULL,  5, -1,  1,  3,  JOF_UINT16)
+OPDEF(JSOP_LEAVEBLOCKEXPR,207,"leaveblockexpr",NULL,  5, -1,  1,  3,  JOF_UINT16)
 \
 /*
  * Optimize atom segments 1-3.  These must be followed by JSOP_RESETBASE0 after
  * the opcode that they prefix.
  */
-OPDEF(JSOP_INDEXBASE1,    209,"indexbase1",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
-OPDEF(JSOP_INDEXBASE2,    210,"indexbase2",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
-OPDEF(JSOP_INDEXBASE3,    211,"indexbase3",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
+OPDEF(JSOP_INDEXBASE1,    208,"indexbase1",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
+OPDEF(JSOP_INDEXBASE2,    209,"indexbase2",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
+OPDEF(JSOP_INDEXBASE3,    210,"indexbase3",    NULL,  1,  0,  0,  0,  JOF_BYTE |JOF_INDEXBASE)
 
-OPDEF(JSOP_CALLGNAME,     212, "callgname",    NULL,  3,  0,  2, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_CALLOP|JOF_GNAME)
-OPDEF(JSOP_CALLLOCAL,     213, "calllocal",    NULL,  3,  0,  2, 19,  JOF_LOCAL|JOF_NAME|JOF_CALLOP)
-OPDEF(JSOP_CALLARG,       214, "callarg",      NULL,  3,  0,  2, 19,  JOF_QARG |JOF_NAME|JOF_CALLOP)
-OPDEF(JSOP_BINDGNAME,     215, "bindgname",    NULL,  3,  0,  1,  0,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME)
+OPDEF(JSOP_CALLGNAME,     211, "callgname",    NULL,  3,  0,  2, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_CALLOP|JOF_GNAME)
+OPDEF(JSOP_CALLLOCAL,     212, "calllocal",    NULL,  3,  0,  2, 19,  JOF_LOCAL|JOF_NAME|JOF_CALLOP)
+OPDEF(JSOP_CALLARG,       213, "callarg",      NULL,  3,  0,  2, 19,  JOF_QARG |JOF_NAME|JOF_CALLOP)
+OPDEF(JSOP_BINDGNAME,     214, "bindgname",    NULL,  3,  0,  1,  0,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME)
 
 /*
  * Opcodes to hold 8-bit and 32-bit immediate integer operands.
  */
-OPDEF(JSOP_INT8,          216, "int8",         NULL,  2,  0,  1, 16,  JOF_INT8)
-OPDEF(JSOP_INT32,         217, "int32",        NULL,  5,  0,  1, 16,  JOF_INT32)
+OPDEF(JSOP_INT8,          215, "int8",         NULL,  2,  0,  1, 16,  JOF_INT8)
+OPDEF(JSOP_INT32,         216, "int32",        NULL,  5,  0,  1, 16,  JOF_INT32)
 
 /*
  * Get the value of the 'length' property from a stacked object.
  */
-OPDEF(JSOP_LENGTH,        218, "length",       NULL,  3,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET)
+OPDEF(JSOP_LENGTH,        217, "length",       NULL,  3,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_TYPESET)
 
 /*
  * Push a JSVAL_HOLE value onto the stack, representing an omitted property in
  * an array literal (e.g. property 0 in the array [, 1]).  This opcode is used
  * with the JSOP_NEWARRAY opcode.
  */
-OPDEF(JSOP_HOLE,          219, "hole",         NULL,  1,  0,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_HOLE,          218, "hole",         NULL,  1,  0,  1,  0,  JOF_BYTE)
 
 /*
  * Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case.
  */
-OPDEF(JSOP_DEFFUN_FC,     220,"deffun_fc",     NULL,  3,  0,  0,  0,  JOF_OBJECT|JOF_DECLARING)
-OPDEF(JSOP_DEFLOCALFUN_FC,221,"deflocalfun_fc",NULL,  5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
-OPDEF(JSOP_LAMBDA_FC,     222,"lambda_fc",     NULL,  3,  0,  1, 19,  JOF_OBJECT)
+OPDEF(JSOP_DEFFUN_FC,     219,"deffun_fc",     NULL,  3,  0,  0,  0,  JOF_OBJECT|JOF_DECLARING)
+OPDEF(JSOP_DEFLOCALFUN_FC,220,"deflocalfun_fc",NULL,  5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
+OPDEF(JSOP_LAMBDA_FC,     221,"lambda_fc",     NULL,  3,  0,  1, 19,  JOF_OBJECT)
 
 /*
  * Ensure that the value on the top of the stack is an object. The one
  * argument is an error message, defined in js.msg, that takes one parameter
  * (the decompilation of the primitive value).
  */
-OPDEF(JSOP_OBJTOP,        223,"objtop",        NULL,  3,  0,  0,  0,  JOF_UINT16)
+OPDEF(JSOP_OBJTOP,        222,"objtop",        NULL,  3,  0,  0,  0,  JOF_UINT16)
 
 /*
  * Joined function object as method optimization support.
  */
-OPDEF(JSOP_SETMETHOD,     224,"setmethod",     NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
-OPDEF(JSOP_INITMETHOD,    225,"initmethod",    NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
+OPDEF(JSOP_SETMETHOD,     223,"setmethod",     NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
+OPDEF(JSOP_INITMETHOD,    224,"initmethod",    NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
 
-OPDEF(JSOP_SHARPINIT,     226,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
+OPDEF(JSOP_SHARPINIT,     225,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
 
 /* Pop the stack, convert to a jsid (int or string), and push back. */
-OPDEF(JSOP_TOID,          227, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_TOID,          226, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -221,17 +221,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 98)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 99)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -955,17 +955,18 @@ mjit::Compiler::finishThisUp(JITScript *
     JSC::LinkBuffer fullCode(result, codeSize, JSC::METHOD_CODE);
     JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size(), JSC::METHOD_CODE);
 
     size_t nNmapLive = loopEntries.length();
     for (size_t i = 0; i < script->length; i++) {
         Bytecode *opinfo = analysis->maybeCode(i);
         if (opinfo && opinfo->safePoint) {
             /* loopEntries cover any safe points which are at loop heads. */
-            if (!cx->typeInferenceEnabled() || !opinfo->loopHead)
+            JSOp op = js_GetOpcode(cx, script, script->code + i);
+            if (!cx->typeInferenceEnabled() || op != JSOP_LOOPHEAD)
                 nNmapLive++;
         }
     }
 
     /* Please keep in sync with JITScript::scriptDataSize! */
     size_t dataSize = sizeof(JITScript) +
                       sizeof(NativeMapEntry) * nNmapLive +
                       sizeof(InlineFrame) * inlineFrames.length() +
@@ -1018,17 +1019,18 @@ mjit::Compiler::finishThisUp(JITScript *
     NativeMapEntry *jitNmap = (NativeMapEntry *)cursor;
     jit->nNmapPairs = nNmapLive;
     cursor += sizeof(NativeMapEntry) * jit->nNmapPairs;
     size_t ix = 0;
     if (jit->nNmapPairs > 0) {
         for (size_t i = 0; i < script->length; i++) {
             Bytecode *opinfo = analysis->maybeCode(i);
             if (opinfo && opinfo->safePoint) {
-                if (cx->typeInferenceEnabled() && opinfo->loopHead)
+                JSOp op = js_GetOpcode(cx, script, script->code + i);
+                if (cx->typeInferenceEnabled() && op == JSOP_LOOPHEAD)
                     continue;
                 Label L = jumpMap[i];
                 JS_ASSERT(L.isSet());
                 jitNmap[ix].bcOff = i;
                 jitNmap[ix].ncode = (uint8 *)(result + masm.distanceOf(L));
                 ix++;
             }
         }
@@ -1577,17 +1579,17 @@ mjit::Compiler::generateMethod()
                 fixedDoubleToAnyEntries.clear();
 
                 /*
                  * Watch for fallthrough to the head of a 'do while' loop.
                  * We don't know what register state we will be using at the head
                  * of the loop so sync, branch, and fix it up after the loop
                  * has been processed.
                  */
-                if (cx->typeInferenceEnabled() && analysis->getCode(PC).loopHead) {
+                if (cx->typeInferenceEnabled() && op == JSOP_LOOPHEAD) {
                     frame.syncAndForgetEverything();
                     Jump j = masm.jump();
                     if (!startLoop(PC, j, PC))
                         return Compile_Error;
                 } else {
                     Label start = masm.label();
                     if (!frame.syncForBranch(PC, Uses(0)))
                         return Compile_Error;
@@ -1730,17 +1732,17 @@ mjit::Compiler::generateMethod()
 
             /*
              * Watch for gotos which are entering a 'for' or 'while' loop.
              * These jump to the loop condition test and are immediately
              * followed by the head of the loop.
              */
             jsbytecode *next = PC + js_CodeSpec[op].length;
             if (cx->typeInferenceEnabled() && analysis->maybeCode(next) &&
-                analysis->getCode(next).loopHead) {
+                js_GetOpcode(cx, script, next) == JSOP_LOOPHEAD) {
                 frame.syncAndForgetEverything();
                 Jump j = masm.jump();
                 if (!startLoop(next, j, target))
                     return Compile_Error;
             } else {
                 if (!frame.syncForBranch(target, Uses(0)))
                     return Compile_Error;
                 Jump j = masm.jump();
@@ -2807,25 +2809,24 @@ mjit::Compiler::generateMethod()
             prepareStubCall(Uses(frame.frameSlots()));
             masm.move(ImmPtr(fun), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::FlatLambda, REJOIN_PUSH_OBJECT);
             frame.takeReg(Registers::ReturnReg);
             frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
           }
           END_CASE(JSOP_LAMBDA_FC)
 
-          BEGIN_CASE(JSOP_TRACE)
-          BEGIN_CASE(JSOP_NOTRACE)
+          BEGIN_CASE(JSOP_LOOPHEAD)
           {
             if (analysis->jumpTarget(PC)) {
                 interruptCheckHelper();
                 recompileCheckHelper();
             }
           }
-          END_CASE(JSOP_TRACE)
+          END_CASE(JSOP_LOOPHEAD)
 
           BEGIN_CASE(JSOP_DEBUGGER)
           {
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(PC), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DebuggerStatement, REJOIN_FALLTHROUGH);
           }
           END_CASE(JSOP_DEBUGGER)
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1777,35 +1777,36 @@ LoopState::analyzeLoopBody(unsigned fram
     temporariesStart =
         Max<uint32>(temporariesStart,
                     ssa->getFrame(frame).depth + VALUES_PER_STACK_FRAME * 2 + script->nslots);
 
     if (script->failedBoundsCheck || analysis->localsAliasStack())
         skipAnalysis = true;
 
     /* Analyze the entire script for frames inlined in the loop body. */
-    unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_TRACE_LENGTH : 0;
+    unsigned start = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->head + JSOP_LOOPHEAD_LENGTH : 0;
     unsigned end = (frame == CrossScriptSSA::OUTER_FRAME) ? lifetime->backedge : script->length;
 
     unsigned offset = start;
     while (offset < end) {
         jsbytecode *pc = script->code + offset;
         uint32 successorOffset = offset + analyze::GetBytecodeLength(pc);
 
         analyze::Bytecode *opinfo = analysis->maybeCode(offset);
         if (!opinfo) {
             offset = successorOffset;
             continue;
         }
 
+        JSOp op = JSOp(*pc);
+
         /* Don't do any hoisting for outer loops in case of nesting. */
-        if (opinfo->loopHead)
+        if (op == JSOP_LOOPHEAD)
             skipAnalysis = true;
 
-        JSOp op = JSOp(*pc);
         switch (op) {
 
           case JSOP_CALL: {
             /*
              * Don't hoist within this loop unless calls at this site are inlined.
              * :XXX: also recognize native calls which will be inlined.
              */
             bool foundInline = false;
@@ -1890,18 +1891,17 @@ LoopState::analyzeLoopBody(unsigned fram
             break;
           }
 
           case JSOP_ENUMELEM:
           case JSOP_ENUMCONSTELEM:
             unknownModset = true;
             break;
 
-          case JSOP_TRACE:
-          case JSOP_NOTRACE:
+          case JSOP_LOOPHEAD:
           case JSOP_POP:
           case JSOP_ZERO:
           case JSOP_ONE:
           case JSOP_INT8:
           case JSOP_INT32:
           case JSOP_UINT16:
           case JSOP_UINT24:
           case JSOP_FALSE:
--- a/js/src/methodjit/TrampolineSparc.s
+++ b/js/src/methodjit/TrampolineSparc.s
@@ -107,24 +107,25 @@ throwpoline_exit:
 .size   JaegerThrowpoline, . - JaegerThrowpoline
 
 ! void JaegerInterpolineScripted()
 .global JaegerInterpolineScripted
 .type   JaegerInterpolineScripted, #function
 JaegerInterpolineScripted:
     ld      [%l0 + 0x10], %l0                        /* Load f->prev_ */
     st      %l0, [%fp - 36]                          /* Update f->regs->fp_ */
-    ba     JaegerInterpoline
+    ba     interpoline_enter
     nop
 .size    JaegerInterpolineScripted, . - JaegerInterpolineScripted
 
 ! void JaegerInterpoline()
 .global JaegerInterpoline
 .type   JaegerInterpoline, #function
 JaegerInterpoline:
+interpoline_enter:
     mov     %o0,%o2
     mov     %l3,%o0
     mov     %l2,%o1
     call    js_InternalInterpret
     mov     %sp,%o3
     ld      [%fp - 36], %l0
     ld      [%l0 + 0x18], %l2                        /* fp->rval type */
     ld      [%l0 + 0x1c], %l3                        /* fp->rval data */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1200,28 +1200,34 @@ GC(JSContext *cx, uintN argc, jsval *vp)
 {
     JSCompartment *comp = NULL;
     if (argc == 1) {
         Value arg = vp[2];
         if (arg.isObject())
             comp = UnwrapObject(&arg.toObject())->compartment();
     }
 
+#ifndef JS_MORE_DETERMINISTIC
     size_t preBytes = cx->runtime->gcBytes;
+#endif
     JS_CompartmentGC(cx, comp);
 
     char buf[256];
+#ifdef JS_MORE_DETERMINISTIC
+    buf[0] = '\0';
+#else
     JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n",
                 (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes,
 #ifdef HAVE_SBRK
                 (unsigned long)sbrk(0)
 #else
                 0
 #endif
                 );
+#endif
     *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
     return true;
 }
 
 static const struct ParamPair {
     const char      *name;
     JSGCParamKey    param;
 } paramMap[] = {
@@ -3816,17 +3822,17 @@ JSBool
 MJitCodeStats(JSContext *cx, uintN argc, jsval *vp)
 {
 #ifdef JS_METHODJIT
     JSRuntime *rt = cx->runtime;
     AutoLockGC lock(rt);
     size_t n = 0, method, regexp, unused;
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
     {
-        (*c)->getMjitCodeStats(method, regexp, unused);
+        (*c)->sizeOfCode(&method, &regexp, &unused);
         n += method + regexp + unused;
     }
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
 #else
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 #endif
     return true;
 }
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -1,11 +1,13 @@
 url-prefix ../../jsreftest.html?test=js1_8_5/regress/
 script no-array-comprehension-length-limit.js
+script regress-373843.js
 script regress-383902.js
+require-or(debugMode,skip) script regress-476088.js
 script regress-500528.js
 script regress-533876.js
 script regress-541255-0.js
 script regress-541255-1.js
 script regress-541255-2.js
 script regress-541255-3.js
 script regress-541255-4.js
 script regress-541455.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-373843.js
@@ -0,0 +1,13 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+if (typeof disassemble != 'undefined')
+{
+    var func = disassemble(function() { return "c\\d"; })
+
+    // The disassembled function will contain a bytecode "string" with the content of the string next to it.
+    // Check if that string isn't over-escaped i.e. \\ isn't escaped to \\\\ .
+    assertEq(func.indexOf("\\\\\\\\"), -1)
+}
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-476088.js
@@ -0,0 +1,17 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+
+var err;
+try {
+    f = function() { var x; x.y; }
+    trap(f, 0, "");
+    f();
+} catch (e) {
+    err = e;
+}
+assertEq(err instanceof TypeError, true);
+assertEq(err.message, "x is undefined")
+
+reportCompare(0, 0, 'ok');
+
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -493,17 +493,17 @@ StackSpace::tryBumpLimit(JSContext *cx, 
 {
     if (!ensureSpace(cx, REPORT_ERROR, from, nvals))
         return false;
     *limit = conservativeEnd_;
     return true;
 }
 
 size_t
-StackSpace::committedSize()
+StackSpace::sizeOfCommitted()
 {
 #ifdef XP_WIN
     return (commitEnd_ - base_) * sizeof(Value);
 #else
     return (trustedEnd_ - base_) * sizeof(Value);
 #endif
 }
 
--- a/js/src/vm/StackSpace.h
+++ b/js/src/vm/StackSpace.h
@@ -176,17 +176,17 @@ class StackSpace
      */
     inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
     bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
 
     /* Called during GC: mark segments, frames, and slots under firstUnused. */
     void mark(JSTracer *trc);
 
     /* We only report the committed size;  uncommitted size is uninteresting. */
-    JS_FRIEND_API(size_t) committedSize();
+    JS_FRIEND_API(size_t) sizeOfCommitted();
 };
 
 /*****************************************************************************/
 
 class ContextStack
 {
     StackSegment *seg_;
     StackSpace *space_;
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/crashtests/705875.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+
+window.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+window.getInterface(null);
+
+</script>
--- a/js/xpconnect/crashtests/crashtests.list
+++ b/js/xpconnect/crashtests/crashtests.list
@@ -29,8 +29,9 @@ load 558979.html
 load 582649.html
 load 601284-1.html
 load 603146-1.html
 load 603858-1.html
 load 608963.html
 load 616930-1.html
 load 639737-1.html
 load 648206-1.html
+load 705875.html
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3897,18 +3897,21 @@ nsXPCComponents_Utils::CreateObjectIn(co
 }
 
 JSBool
 FunctionWrapper(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval v = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0);
     NS_ASSERTION(JSVAL_IS_OBJECT(v), "weird function");
 
-    return JS_CallFunctionValue(cx, JS_THIS_OBJECT(cx, vp), v,
-                                argc, JS_ARGV(cx, vp), vp);
+    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj) {
+        return JS_FALSE;
+    }
+    return JS_CallFunctionValue(cx, obj, v, argc, JS_ARGV(cx, vp), vp);
 }
 
 JSBool
 WrapCallable(JSContext *cx, JSObject *obj, jsid id, JSObject *propobj, jsval *vp)
 {
     JSFunction *fun = js::NewFunctionByIdWithReserved(cx, FunctionWrapper, 0, 0,
                                                       JS_GetGlobalForObject(cx, obj), id);
     if (!fun)
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -596,19 +596,21 @@ XPCConvert::JSData2Native(XPCCallContext
             XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
             NS_ERROR("void* params not supported");
             return false;
         case nsXPTType::T_IID:
         {
             JSObject* obj;
             const nsID* pid=nsnull;
 
+            // There's no good reason to pass a null IID.
             if (JSVAL_IS_VOID(s) || JSVAL_IS_NULL(s)) {
-                *((const nsID**)d) = nsnull;
-                return true;
+                if (pErr)
+                  *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+                return false;
             }
 
             if (!JSVAL_IS_OBJECT(s) ||
                 (!(obj = JSVAL_TO_OBJECT(s))) ||
                 (!(pid = xpc_JSObjectToID(cx, obj))) ||
                 (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
                 return false;
             }
@@ -1029,16 +1031,19 @@ XPCConvert::NativeInterface2JSObject(XPC
             return false;
 
         jsval slim;
         if (ConstructSlimWrapper(ccx, aHelper, xpcscope, &slim)) {
             *d = slim;
             return true;
         }
 
+        if (JS_IsExceptionPending(cx))
+            return false;
+
         // Even if ConstructSlimWrapper returns false it might have created a
         // wrapper (while calling the PreCreate hook). In that case we need to
         // fall through because we either have a slim wrapper that needs to be
         // morphed or an XPCWrappedNative.
         flat = cache->GetWrapper();
     }
 
     // We can't simply construct a slim wrapper. Go ahead and create an
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1223,20 +1223,19 @@ CompartmentCallback(JSContext *cx, void 
     CompartmentStats compartmentStats(cx, compartment);
     CompartmentStats *curr =
         data->compartmentStatsVector.AppendElement(compartmentStats);
     data->currCompartmentStats = curr;
 
     // Get the compartment-level numbers.
 #ifdef JS_METHODJIT
     size_t method, regexp, unused;
-    compartment->getMjitCodeStats(method, regexp, unused);
-    curr->mjitCodeMethod = method;
-    curr->mjitCodeRegexp = regexp;
-    curr->mjitCodeUnused = unused;
+    compartment->sizeOfCode(&method, &regexp, &unused);
+    JS_ASSERT(regexp == 0);     /* this execAlloc is only used for method code */
+    curr->mjitCode = method + unused;
 #endif
     JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory,
                                    MemoryReporterMallocSizeOf);
     curr->shapesCompartmentTables =
         js::GetCompartmentShapeTableSize(compartment, MemoryReporterMallocSizeOf);
 }
 
 void
@@ -1534,25 +1533,53 @@ CollectCompartmentStatsForRuntime(JSRunt
         data->gcHeapChunkTotal =
             PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
             js::gc::ChunkSize;
 
         js::IterateCompartmentsArenasCells(cx, data, CompartmentCallback,
                                            ArenaCallback, CellCallback);
         js::IterateChunks(cx, data, ChunkCallback);
 
-        for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
-            data->stackSize += i.threadData()->stackSpace.committedSize();
-
-        data->runtimeObjectSize = MemoryReporterMallocSizeOf(rt, sizeof(JSRuntime));
+        data->runtimeObject = MemoryReporterMallocSizeOf(rt, sizeof(JSRuntime));
 
         // Nb: we use sizeOfExcludingThis() because atomState.atoms is within
         // JSRuntime, and so counted when JSRuntime is counted.
-        data->atomsTableSize =
+        data->runtimeAtomsTable =
             rt->atomState.atoms.sizeOfExcludingThis(MemoryReporterMallocSizeOf);
+
+        {
+            #ifndef JS_THREADSAFE
+            #error "This code assumes JS_THREADSAFE is defined"
+            #endif
+
+            // Need the GC lock to call JS_ContextIteratorUnlocked() and to
+            // access rt->threads.
+            js::AutoLockGC lock(rt);
+
+            JSContext *acx, *iter = NULL;
+            while ((acx = JS_ContextIteratorUnlocked(rt, &iter)) != NULL) {
+                data->runtimeContexts +=
+                    acx->sizeOfIncludingThis(MemoryReporterMallocSizeOf);
+            }
+
+            for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
+                JSThread *thread = r.front().value;
+                size_t normal, temporary, regexpCode, stackCommitted;
+                thread->sizeOfIncludingThis(MemoryReporterMallocSizeOf,
+                                            &normal,
+                                            &temporary,
+                                            &regexpCode,
+                                            &stackCommitted);
+
+                data->runtimeThreadsNormal         += normal;
+                data->runtimeThreadsTemporary      += temporary;
+                data->runtimeThreadsRegexpCode     += regexpCode;
+                data->runtimeThreadsStackCommitted += stackCommitted;
+            }
+        }
     }
 
     JS_DestroyContextNoGC(cx);
 
     // This is initialized to all bytes stored in used chunks, and then we
     // subtract used space from it each time around the loop.
     data->gcHeapChunkDirtyUnused = data->gcHeapChunkTotal -
                                    data->gcHeapChunkCleanUnused -
@@ -1587,19 +1614,17 @@ CollectCompartmentStatsForRuntime(JSRunt
                               stats.shapesExtraTreeTables +
                               stats.shapesExtraDictTables +
                               stats.shapesCompartmentTables;
         data->totalScripts += stats.gcHeapScripts + 
                               stats.scriptData;
         data->totalStrings += stats.gcHeapStrings + 
                               stats.stringChars;
 #ifdef JS_METHODJIT
-        data->totalMjit    += stats.mjitCodeMethod + 
-                              stats.mjitCodeRegexp +
-                              stats.mjitCodeUnused +
+        data->totalMjit    += stats.mjitCode + 
                               stats.mjitData;
 #endif
         data->totalTypeInference += stats.gcHeapTypeObjects +
                                     stats.typeInferenceMemory.objects +
                                     stats.typeInferenceMemory.scripts + 
                                     stats.typeInferenceMemory.tables;
         data->totalAnalysisTemp  += stats.typeInferenceMemory.temporary;
     }
@@ -1775,35 +1800,22 @@ ReportCompartmentStats(const Compartment
                                               "script-data"),
                        nsIMemoryReporter::KIND_HEAP, stats.scriptData,
                        "Memory allocated for JSScript bytecode and various variable-length "
                        "tables." SLOP_BYTES_STRING,
                        callback, closure);
 
 #ifdef JS_METHODJIT
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
-                                              "mjit-code/method"),
-                       nsIMemoryReporter::KIND_NONHEAP, stats.mjitCodeMethod,
+                                              "mjit-code"),
+                       nsIMemoryReporter::KIND_NONHEAP, stats.mjitCode,
                        "Memory used by the method JIT to hold the compartment's generated code.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
-                                              "mjit-code/regexp"),
-                       nsIMemoryReporter::KIND_NONHEAP, stats.mjitCodeRegexp,
-                       "Memory used by the regexp JIT to hold the compartment's generated code.",
-                       callback, closure);
-
-    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
-                                              "mjit-code/unused"),
-                       nsIMemoryReporter::KIND_NONHEAP, stats.mjitCodeUnused,
-                       "Memory allocated by the method and/or regexp JIT to hold the "
-                       "compartment's code, but which is currently unused.",
-                       callback, closure);
-
-    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "mjit-data"),
                        nsIMemoryReporter::KIND_HEAP, stats.mjitData,
                        "Memory used by the method JIT for the compartment's compilation data: "
                        "JITScripts, native maps, and inline cache structs." SLOP_BYTES_STRING,
                        callback, closure);
 #endif
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
@@ -1846,30 +1858,55 @@ ReportJSRuntimeStats(const IterateData &
     for (PRUint32 index = 0;
          index < data.compartmentStatsVector.Length();
          index++) {
         ReportCompartmentStats(data.compartmentStatsVector[index], pathPrefix,
                                callback, closure);
     }
 
     ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/runtime-object"),
-                      nsIMemoryReporter::KIND_NONHEAP, data.runtimeObjectSize,
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeObject,
                       "Memory used by the JSRuntime object." SLOP_BYTES_STRING,
                       callback, closure);
 
     ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/atoms-table"),
-                      nsIMemoryReporter::KIND_NONHEAP, data.atomsTableSize,
-                      "Memory used by the atoms table.",
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeAtomsTable,
+                      "Memory used by the atoms table." SLOP_BYTES_STRING,
+                      callback, closure);
+
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/contexts"),
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeContexts,
+                      "Memory used by JSContext objects and certain structures "
+                      "hanging off them."  SLOP_BYTES_STRING,
+                      callback, closure);
+
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/normal"),
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsNormal,
+                      "Memory used by JSThread objects and their data, "
+                      "excluding memory that is reported by "
+                      "other reporters under 'explicit/js/runtime/'." SLOP_BYTES_STRING,
                       callback, closure);
 
-    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("stack"),
-                      nsIMemoryReporter::KIND_NONHEAP, data.stackSize,
-                      "Memory used for the JavaScript stack.  This is the committed portion "
-                      "of the stack; any uncommitted portion is not measured because it "
-                      "hardly costs anything.",
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/temporary"),
+                      nsIMemoryReporter::KIND_HEAP, data.runtimeThreadsTemporary,
+                      "Memory held transiently in JSThreads and used during "
+                      "compilation.  It mostly holds parse nodes."
+                      SLOP_BYTES_STRING,
+                      callback, closure);
+
+    ReportMemoryBytes0(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/regexp-code"),
+                       nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsRegexpCode,
+                       "Memory used by the regexp JIT to hold generated code.",
+                       callback, closure);
+
+    ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/threads/stack-committed"),
+                      nsIMemoryReporter::KIND_NONHEAP, data.runtimeThreadsStackCommitted,
+                      "Memory used for the thread stacks.  This is the committed portions "
+                      "of the stacks; any uncommitted portions are not measured because they "
+                      "hardly cost anything.",
                       callback, closure);
 
     ReportMemoryBytes(pathPrefix +
                       NS_LITERAL_CSTRING("gc-heap-chunk-dirty-unused"),
                       JS_GC_HEAP_KIND, data.gcHeapChunkDirtyUnused,
                       "Memory on the garbage-collected JavaScript heap, within chunks with at "
                       "least one allocated GC thing, that could be holding useful data but "
                       "currently isn't.  Memory here is mutually exclusive with memory reported"
@@ -1994,30 +2031,29 @@ public:
                           nsIMemoryReporter::KIND_OTHER, data.totalStrings,
                           "Memory used for all string-related data.  This is the sum of all "
                           "compartments' 'gc-heap/strings' and 'string-chars' numbers.",
                           callback, closure);
 #ifdef JS_METHODJIT
         ReportMemoryBytes(NS_LITERAL_CSTRING("js-total-mjit"),
                           nsIMemoryReporter::KIND_OTHER, data.totalMjit,
                           "Memory used by the method JIT.  This is the sum of all compartments' "
-                          "'mjit-code-method', 'mjit-code-regexp', 'mjit-code-unused' and '"
-                          "'mjit-data' numbers.",
+                          "'mjit-code', and 'mjit-data' numbers.",
                           callback, closure);
 #endif
         ReportMemoryBytes(NS_LITERAL_CSTRING("js-total-type-inference"),
                           nsIMemoryReporter::KIND_OTHER, data.totalTypeInference,
                           "Non-transient memory used by type inference.  This is the sum of all "
                           "compartments' 'gc-heap/type-objects', 'type-inference/script-main', "
                           "'type-inference/object-main' and 'type-inference/tables' numbers.",
                           callback, closure);
 
         ReportMemoryBytes(NS_LITERAL_CSTRING("js-total-analysis-temporary"),
                           nsIMemoryReporter::KIND_OTHER, data.totalAnalysisTemp,
-                          "Transient memory used during type inference and compilation. "
+                          "Memory used transiently during type inference and compilation. "
                           "This is the sum of all compartments' 'analysis-temporary' numbers.",
                           callback, closure);
 
         return NS_OK;
     }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(XPConnectJSCompartmentsMultiReporter
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -216,30 +216,32 @@ struct CompartmentStats
     PRInt64 stringChars;
     PRInt64 shapesExtraTreeTables;
     PRInt64 shapesExtraDictTables;
     PRInt64 shapesExtraTreeShapeKids;
     PRInt64 shapesCompartmentTables;
     PRInt64 scriptData;
 
 #ifdef JS_METHODJIT
-    PRInt64 mjitCodeMethod;
-    PRInt64 mjitCodeRegexp;
-    PRInt64 mjitCodeUnused;
+    PRInt64 mjitCode;
     PRInt64 mjitData;
 #endif
     TypeInferenceMemoryStats typeInferenceMemory;
 };
 
 struct IterateData
 {
     IterateData()
-      : runtimeObjectSize(0),
-        atomsTableSize(0),
-        stackSize(0),
+      : runtimeObject(0),
+        runtimeAtomsTable(0),
+        runtimeContexts(0),
+        runtimeThreadsNormal(0),
+        runtimeThreadsTemporary(0),
+        runtimeThreadsRegexpCode(0),
+        runtimeThreadsStackCommitted(0),
         gcHeapChunkTotal(0),
         gcHeapChunkCleanUnused(0),
         gcHeapChunkDirtyUnused(0),
         gcHeapChunkCleanDecommitted(0),
         gcHeapChunkDirtyDecommitted(0),
         gcHeapArenaUnused(0),
         gcHeapChunkAdmin(0),
         gcHeapUnusedPercentage(0),
@@ -250,19 +252,23 @@ struct IterateData
 #ifdef JS_METHODJIT
         totalMjit(0),
 #endif
         totalTypeInference(0),
         totalAnalysisTemp(0),
         compartmentStatsVector(),
         currCompartmentStats(NULL) { }
 
-    PRInt64 runtimeObjectSize;
-    PRInt64 atomsTableSize;
-    PRInt64 stackSize;
+    PRInt64 runtimeObject;
+    PRInt64 runtimeAtomsTable;
+    PRInt64 runtimeContexts;
+    PRInt64 runtimeThreadsNormal;
+    PRInt64 runtimeThreadsTemporary;
+    PRInt64 runtimeThreadsRegexpCode;
+    PRInt64 runtimeThreadsStackCommitted;
     PRInt64 gcHeapChunkTotal;
     PRInt64 gcHeapChunkCleanUnused;
     PRInt64 gcHeapChunkDirtyUnused;
     PRInt64 gcHeapChunkCleanDecommitted;
     PRInt64 gcHeapChunkDirtyDecommitted;
     PRInt64 gcHeapArenaUnused;
     PRInt64 gcHeapChunkAdmin;
     PRInt64 gcHeapUnusedPercentage;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -445,17 +445,17 @@ nodePrincipal_getter(JSContext *cx, JSOb
     }
     return true;
 }
 
 static JSBool
 XrayToString(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *wrapper = JS_THIS_OBJECT(cx, vp);
-    if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
+    if (!wrapper || !IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     nsAutoString result(NS_LITERAL_STRING("[object XrayWrapper "));
     if (mozilla::dom::binding::instanceIsProxy(&js::GetProxyPrivate(wrapper).toObject())) {
         JSString *wrapperStr = js::GetProxyHandler(wrapper)->obj_toString(cx, wrapper);
         size_t length;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1883,24 +1883,29 @@ DocumentViewerImpl::SetBounds(const nsIn
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 
   mBounds = aBounds;
   if (mWindow) {
     // When attached to a top level window, change the client area, not the
     // window frame.
     // Don't have the widget repaint. Layout will generate repaint requests
     // during reflow.
-    if (mAttachedToParent)
-      mWindow->ResizeClient(aBounds.x, aBounds.y,
-                            aBounds.width, aBounds.height,
-                            false);
-    else
+    if (mAttachedToParent) {
+      if (aBounds.x != 0 || aBounds.y != 0) {
+        mWindow->ResizeClient(aBounds.x, aBounds.y,
+                              aBounds.width, aBounds.height,
+                              false);
+      } else {
+        mWindow->ResizeClient(aBounds.width, aBounds.height, false);
+      }
+    } else {
       mWindow->Resize(aBounds.x, aBounds.y,
                       aBounds.width, aBounds.height,
                       false);
+    }
   } else if (mPresContext && mViewManager) {
     PRInt32 p2a = mPresContext->AppUnitsPerDevPixel();
     mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(mBounds.width, p2a),
                                       NSIntPixelsToAppUnits(mBounds.height, p2a));
   }
 
   // If there's a previous viewer, it's the one that's actually showing,
   // so be sure to resize it as well so it paints over the right area.
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -96,17 +96,16 @@ function endTest() {
 
 var isWindows = /WINNT/.test(SpecialPowers.OS);
 
 var tests = [
     [ 'bug389321-2.html' , 'bug389321-2-ref.html' ] ,
     [ 'bug389321-3.html' , 'bug389321-3-ref.html' ] ,
     [ 'bug482484.html'   , 'bug482484-ref.html'   ] ,
     [ 'bug585922.html'   , 'bug585922-ref.html'   ] ,
-    [ 'bug602141-3.html' , 'bug602141-3-ref.html' ] ,
     [ 'bug632215-1.html' , 'bug632215-ref.html'   ] ,
     [ 'bug632215-2.html' , 'bug632215-ref.html'   ] ,
     [ 'bug633044-1.html' , 'bug633044-1-ref.html' ] ,
     [ 'bug644428-1.html' , 'bug644428-1-ref.html' ] ,
 ];
 
 if (!isWindows) {
   tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837
@@ -114,16 +113,17 @@ if (!isWindows) {
   tests.push([ 'bug240933-1.html' , 'bug240933-1-ref.html' ]); // bug 681144
   tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
   tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163
   tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
   tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
   tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
   tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
   tests.push([ 'bug602141-2.html' , 'bug602141-2-ref.html' ]); // bug 682836
+  tests.push([ 'bug602141-3.html' , 'bug602141-3-ref.html' ]); // bug 683048
   tests.push([ 'bug602141-4.html' , 'bug602141-4-ref.html' ]); // bug 681167
   tests.push([ 'bug612271-1.html' , 'bug612271-ref.html' ]);   // bug 681032
   tests.push([ 'bug612271-2.html' , 'bug612271-ref.html' ]);   // bug 680581
   tests.push([ 'bug612271-3.html' , 'bug612271-ref.html' ]);   // bug 681035
   tests.push([ 'bug613433-1.html' , 'bug613433-ref.html' ]);   // bug 681332
   tests.push([ 'bug613433-2.html' , 'bug613433-ref.html' ]);   // bug 681332
   tests.push([ 'bug613433-3.html' , 'bug613433-ref.html' ]);   // bug 681332
   tests.push([ 'bug613807-1.html' , 'bug613807-1-ref.html' ]); // bug 680574
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -86,23 +86,19 @@
 // {3B581FD4-3497-426c-8F61-3658B971CB80}
 #define NS_TREEBOXOBJECT_CID \
 { 0x3b581fd4, 0x3497, 0x426c, { 0x8f, 0x61, 0x36, 0x58, 0xb9, 0x71, 0xcb, 0x80 } }
 
 // {a35d1cd4-c505-4d2d-a0f9-aef00b7ce5a5}
 #define NS_CANVASRENDERINGCONTEXT2D_CID \
 { 0xa35d1cd4, 0xc505, 0x4d2d, { 0xa0, 0xf9, 0xae, 0xf0, 0x0b, 0x7c, 0xe5, 0xa5 } }
 
-// {BCD923C0-9788-4350-AC48-365F473161EB}
-#define NS_CANVASRENDERINGCONTEXT2DTHEBES_CID \
-{ 0xbcd923c0, 0x9788, 0x4350, { 0xac, 0x48, 0x36, 0x5f, 0x47, 0x31, 0x61, 0xeb } }
-
-// {9052bb12-79b0-4bdd-8e60-7bf078026b6d}
-#define NS_CANVASRENDERINGCONTEXT2DAZURE_CID \
-{0x9052bb12, 0x79b0, 0x4bdd, {0x8e, 0x60, 0x7b, 0xf0, 0x78, 0x02, 0x6b, 0x6d}}
+// {BCD923C0-9788-4350-AC48-365F473161EB}
+#define NS_CANVASRENDERINGCONTEXT2DTHEBES_CID \
+{ 0xbcd923c0, 0x9788, 0x4350, { 0xac, 0x48, 0x36, 0x5f, 0x47, 0x31, 0x61, 0xeb } }
 
 // {2fe88332-31c6-4829-b247-a07d8a73e80f}
 #define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
 { 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
 
 // {8b449142-1eab-4bfa-9830-fab6ebb09774}
 #define NS_DOMSTORAGE_CID \
 { 0x8b449142, 0x1eab, 0x4bfa, { 0x98, 0x30, 0xfa, 0xb6, 0xeb, 0xb0, 0x97, 0x74 } }
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -760,17 +760,16 @@ NS_DEFINE_NAMED_CID(NS_PRECONTENTITERATO
 NS_DEFINE_NAMED_CID(NS_SUBTREEITERATOR_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLIMAGEELEMENT_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLOPTIONELEMENT_CID);
 #ifdef MOZ_MEDIA
 NS_DEFINE_NAMED_CID(NS_HTMLAUDIOELEMENT_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_CANVASRENDERINGCONTEXT2D_CID);
 NS_DEFINE_NAMED_CID(NS_CANVASRENDERINGCONTEXT2DTHEBES_CID);
-NS_DEFINE_NAMED_CID(NS_CANVASRENDERINGCONTEXT2DAZURE_CID);
 NS_DEFINE_NAMED_CID(NS_CANVASRENDERINGCONTEXTWEBGL_CID);
 NS_DEFINE_NAMED_CID(NS_TEXT_ENCODER_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLCOPY_TEXT_ENCODER_CID);
 NS_DEFINE_NAMED_CID(NS_XMLCONTENTSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_XHTMLCONTENTSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLCONTENTSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_PLAINTEXTSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(MOZ_SANITIZINGHTMLSERIALIZER_CID);
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -7079,30 +7079,63 @@ nsTextFrame::Reflow(nsPresContext*      
   ReflowText(*aReflowState.mLineLayout, aReflowState.availableWidth,
              aReflowState.rendContext, aReflowState.mFlags.mBlinks,
              aMetrics, aStatus);
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
   return NS_OK;
 }
 
+#ifdef ACCESSIBILITY
+/**
+ * Notifies accessibility about text reflow. Used by nsTextFrame::ReflowText.
+ */
+class NS_STACK_CLASS ReflowTextA11yNotifier
+{
+public:
+  ReflowTextA11yNotifier(nsPresContext* aPresContext, nsIContent* aContent) :
+    mPresContext(aPresContext), mContent(aContent)
+  {
+  }
+  ~ReflowTextA11yNotifier()
+  {
+    nsAccessibilityService* accService = nsIPresShell::AccService();
+    if (accService) {
+      accService->UpdateText(mPresContext->PresShell(), mContent);
+    }
+  }
+private:
+  ReflowTextA11yNotifier();
+  ReflowTextA11yNotifier(const ReflowTextA11yNotifier&);
+  ReflowTextA11yNotifier& operator =(const ReflowTextA11yNotifier&);
+
+  nsIContent* mContent;
+  nsPresContext* mPresContext;
+};
+#endif
+
 void
 nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
                         nsRenderingContext* aRenderingContext,
                         bool aShouldBlink,
                         nsHTMLReflowMetrics& aMetrics,
                         nsReflowStatus& aStatus)
 {
 #ifdef NOISY_REFLOW
   ListTag(stdout);
   printf(": BeginReflow: availableWidth=%d\n", aAvailableWidth);
 #endif
 
   nsPresContext* presContext = PresContext();
 
+#ifdef ACCESSIBILITY
+  // Schedule the update of accessible tree since rendered text might be changed.
+  ReflowTextA11yNotifier(presContext, mContent);
+#endif
+
   /////////////////////////////////////////////////////////////////////
   // Set up flags and clear out state
   /////////////////////////////////////////////////////////////////////
 
   // Clear out the reflow state flags in mState (without destroying
   // the TEXT_BLINK_ON bit). We also clear the whitespace flags because this
   // can change whether the frame maps whitespace-only text or not.
   RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
@@ -7603,24 +7636,16 @@ nsTextFrame::ReflowText(nsLineLayout& aL
   Invalidate(aMetrics.VisualOverflow());
 
 #ifdef NOISY_REFLOW
   ListTag(stdout);
   printf(": desiredSize=%d,%d(b=%d) status=%x\n",
          aMetrics.width, aMetrics.height, aMetrics.ascent,
          aStatus);
 #endif
-
-#ifdef ACCESSIBILITY
-  // Schedule the update of accessible tree when rendered text might be changed.
-  nsAccessibilityService* accService = nsIPresShell::AccService();
-  if (accService) {
-    accService->UpdateText(presContext->PresShell(), mContent);
-  }
-#endif
 }
 
 /* virtual */ bool
 nsTextFrame::CanContinueTextRun() const
 {
   // We can continue a text run through a text frame
   return true;
 }
--- a/layout/reftests/bugs/491323-1-ref.xul
+++ b/layout/reftests/bugs/491323-1-ref.xul
@@ -1,8 +1,15 @@
 <?xml version="1.0"?>
 <window xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <hbox flex="1">
+  <html:style>
+    /* Make sure our splitter's background doesn't depend on its hover styles */
+    splitter {
+      background-color: yellow;
+      -moz-appearance: none;
+    }
+  </html:style>
   <splitter collapse="before" resizeafter="farthest" width="200" id="s">
   </splitter>
 </hbox>
 </window>
--- a/layout/reftests/bugs/491323-1.xul
+++ b/layout/reftests/bugs/491323-1.xul
@@ -1,13 +1,20 @@
 <?xml version="1.0"?>
 <window xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="loaded()">
 <hbox flex="1">
+  <html:style>
+    /* Make sure our splitter's background doesn't depend on its hover styles */
+    splitter {
+      background-color: yellow;
+      -moz-appearance: none;
+    }
+  </html:style>
   <script>
   function loaded() {
     document.documentElement.getBoundingClientRect();
     document.getElementById("s").setAttribute("state", "collapsed");
   }
   </script>
   <iframe width="200" src="data:text/html,hello"/>
   <splitter collapse="before" resizeafter="farthest" width="200" id="s">
new file mode 100644
--- /dev/null
+++ b/layout/tables/crashtests/451355-1.html
@@ -0,0 +1,5 @@
+<style>table::after { content:"m"; }</style>
+<table>
+<select></select>
+<th></th>
+<colgroup>
new file mode 100644
--- /dev/null
+++ b/layout/tables/crashtests/705996-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('x').setAttribute('rowspan', '3');">
+<table style="border-collapse: collapse;"><td colspan="3" id="x"></td></table>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/tables/crashtests/705996-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="var a = document.getElementById('a'); a.parentNode.removeChild(a);">
+<table style="border-collapse: collapse;"><tr><td colspan="2" id="a"></td><td></td></tr></table>
+</body>
+</html>
--- a/layout/tables/crashtests/crashtests.list
+++ b/layout/tables/crashtests/crashtests.list
@@ -89,16 +89,17 @@ load 416845-1.xhtml
 load 416845-2.xhtml
 load 416845-3.html   
 asserts(12) load 420654-1.xhtml # Bug 575011
 load 423514-1.xhtml
 load 430374.html
 load 444431-1.html
 load 448988-1.xhtml
 load 450311-1.html
+load 451355-1.html
 load 456041.html
 load 457115.html
 load 460637-1.xhtml
 load 460637-2.xhtml
 load 460637-3.xhtml
 load 467141-1.html
 load 488388-1.html
 load 512749-1.html
@@ -109,8 +110,10 @@ load 573354-1.xhtml
 load 576890-1.html
 load 576890-2.html
 load 576890-3.html
 asserts(0-3) load 595758-1.xhtml # Bug 453871
 load 595758-2.xhtml
 load 678447-1.html
 load 691824-1.xhtml
 load 695430-1.html
+load 705996-1.html
+load 705996-2.html
--- a/layout/tables/nsCellMap.cpp
+++ b/layout/tables/nsCellMap.cpp
@@ -2175,19 +2175,16 @@ void nsCellMap::ShrinkWithoutCell(nsTabl
 
   // get the rowspan and colspan from the cell map since the content may have changed
   bool zeroColSpan;
   PRUint32 numCols = aMap.GetColCount();
   PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, false);
   PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan);
   PRUint32 endRowIndex = aRowIndex + rowSpan - 1;
   PRUint32 endColIndex = aColIndex + colSpan - 1;
-  SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex,
-                NS_MAX(0, aMap.GetColCount() - aColIndex - 1),
-                1 + endRowIndex - aRowIndex, aDamageArea);
 
   if (aMap.mTableFrame.HasZeroColSpans()) {
     aMap.mTableFrame.SetNeedColSpanExpansion(true);
   }
 
   // adjust the col counts due to the deleted cell before removing it
   for (colX = aColIndex; colX <= endColIndex; colX++) {
     nsColInfo* colInfo = aMap.GetColInfoAt(colX);
@@ -2248,16 +2245,19 @@ void nsCellMap::ShrinkWithoutCell(nsTabl
           if (colInfo) {
             colInfo->mNumCellsSpan--;
           }
         }
       }
     }
   }
   aMap.RemoveColsAtEnd();
+  SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex,
+                NS_MAX(0, aMap.GetColCount() - aColIndex - 1),
+                1 + endRowIndex - aRowIndex, aDamageArea);
 }
 
 void
 nsCellMap::RebuildConsideringRows(nsTableCellMap&             aMap,
                                   PRInt32                     aStartRowIndex,
                                   nsTArray<nsTableRowFrame*>* aRowsToInsert,
                                   PRInt32                     aNumRowsToRemove)
 {
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -110,16 +110,17 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
 
 //
 // nsMenuPopupFrame ctor
 //
 nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext)
   :nsBoxFrame(aShell, aContext),
   mCurrentMenu(nsnull),
   mPrefSize(-1, -1),
+  mLastClientOffset(0, 0),
   mPopupType(ePopupTypePanel),
   mPopupState(ePopupClosed),
   mPopupAlignment(POPUPALIGNMENT_NONE),
   mPopupAnchor(POPUPALIGNMENT_NONE),
   mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
   mFlipBoth(false),
   mIsOpenChanged(false),
   mIsContextMenu(false),
@@ -481,31 +482,16 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayou
 
   nsPresContext* pc = PresContext();
   if (isOpen) {
     nsIView* view = GetView();
     nsIViewManager* viewManager = view->GetViewManager();
     nsRect rect = GetRect();
     rect.x = rect.y = 0;
 
-    // Increase the popup's view size to account for any titlebar or borders.
-    // XXXndeakin this should really be accounted for earlier in
-    // SetPopupPosition so that this extra size is accounted for when flipping
-    // or resizing the popup due to it being too large, but that can be a
-    // followup bug.
-    if (mPopupType == ePopupTypePanel && view) {
-      nsIWidget* widget = view->GetWidget();
-      if (widget) {
-        nsIntSize popupSize = nsIntSize(pc->AppUnitsToDevPixels(rect.width),
-                                        pc->AppUnitsToDevPixels(rect.height));
-        popupSize = widget->ClientToWindowSize(popupSize);
-        rect.width = pc->DevPixelsToAppUnits(popupSize.width);
-        rect.height = pc->DevPixelsToAppUnits(popupSize.height);
-      }
-    }
     viewManager->ResizeView(view, rect);
 
     viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
     mPopupState = ePopupOpenAndVisible;
     nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0);
   }
 
   // finally, if the popup just opened, send a popupshown event
@@ -1313,30 +1299,31 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
   nsPoint viewPoint = screenPoint - rootScreenRect.TopLeft();
 
   // snap the view's position to device pixels, see bug 622507
   viewPoint.x = presContext->RoundAppUnitsToNearestDevPixels(viewPoint.x);
   viewPoint.y = presContext->RoundAppUnitsToNearestDevPixels(viewPoint.y);
 
   nsIView* view = GetView();
   NS_ASSERTION(view, "popup with no view");
-  presContext->GetPresShell()->GetViewManager()->
-    MoveViewTo(view, viewPoint.x, viewPoint.y);
 
   // Offset the position by the width and height of the borders and titlebar.
   // Even though GetClientOffset should return (0, 0) when there is no
   // titlebar or borders, we skip these calculations anyway for non-panels
   // to save time since they will never have a titlebar.
   nsIWidget* widget = view->GetWidget();
   if (mPopupType == ePopupTypePanel && widget) {
-    nsIntPoint offset = widget->GetClientOffset();
-    viewPoint.x += presContext->DevPixelsToAppUnits(offset.x);
-    viewPoint.y += presContext->DevPixelsToAppUnits(offset.y);
+    mLastClientOffset = widget->GetClientOffset();
+    viewPoint.x += presContext->DevPixelsToAppUnits(mLastClientOffset.x);
+    viewPoint.y += presContext->DevPixelsToAppUnits(mLastClientOffset.y);
   }
 
+  presContext->GetPresShell()->GetViewManager()->
+    MoveViewTo(view, viewPoint.x, viewPoint.y);
+
   // Now that we've positioned the view, sync up the frame's origin.
   nsBoxFrame::SetPosition(viewPoint - GetParent()->GetOffsetTo(rootFrame));
 
   if (sizedToPopup) {
     nsBoxLayoutState state(PresContext());
     // XXXndeakin can parentSize.width still extend outside?
     SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height));
   }
@@ -1758,26 +1745,24 @@ nsMenuPopupFrame::LockMenuUntilClosed(bo
   if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
     nsMenuParent* parentParent = static_cast<nsMenuFrame*>(parent)->GetMenuParent();
     if (parentParent) {
       parentParent->LockMenuUntilClosed(aLock);
     }
   }
 }
 
-NS_IMETHODIMP
-nsMenuPopupFrame::GetWidget(nsIWidget **aWidget)
+nsIWidget*
+nsMenuPopupFrame::GetWidget()
 {
   nsIView * view = GetRootViewForPopup(this);
   if (!view)
-    return NS_OK;
+    return nsnull;
 
-  *aWidget = view->GetWidget();
-  NS_IF_ADDREF(*aWidget);
-  return NS_OK;
+  return view->GetWidget();
 }
 
 void
 nsMenuPopupFrame::AttachedDismissalListener()
 {
   mConsumeRollupEvent = nsIPopupBoxObject::ROLLUP_DEFAULT;
 }
 
@@ -1866,18 +1851,21 @@ nsMenuPopupFrame::DestroyFrom(nsIFrame* 
 
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
 
 
 void
 nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop, bool aUpdateAttrs)
 {
-  if (mScreenXPos == aLeft && mScreenYPos == aTop)
+  nsIWidget* widget = GetWidget();
+  if ((mScreenXPos == aLeft && mScreenYPos == aTop) &&
+      (!widget || widget->GetClientOffset() == mLastClientOffset)) {
     return;
+  }
 
   // reposition the popup at the specified coordinates. Don't clear the anchor
   // and position, because the popup can be reset to its anchor position by
   // using (-1, -1) as coordinates. Subtract off the margin as it will be
   // added to the position when SetPopupPosition is called.
   nsMargin margin(0, 0, 0, 0);
   GetStyleMargin()->GetMargin(margin);
   nsPresContext* presContext = PresContext();
--- a/layout/xul/base/src/nsMenuPopupFrame.h
+++ b/layout/xul/base/src/nsMenuPopupFrame.h
@@ -171,17 +171,17 @@ public:
 
   virtual bool IsContextMenu() { return mIsContextMenu; }
 
   virtual bool MenuClosed() { return true; }
 
   virtual void LockMenuUntilClosed(bool aLock);
   virtual bool IsMenuLocked() { return mIsMenuLocked; }
 
-  NS_IMETHOD GetWidget(nsIWidget **aWidget);
+  nsIWidget* GetWidget();
 
   // The dismissal listener gets created and attached to the window.
   void AttachedDismissalListener();
 
   // Overridden methods
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
@@ -347,16 +347,19 @@ public:
   nsIContent* GetAnchor() const { return mAnchorContent; }
 
   // Return the screen coordinates of the popup, or (-1, -1) if anchored.
   nsIntPoint ScreenPosition() const { return nsIntPoint(mScreenXPos, mScreenYPos); }
 
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
+
+  nsIntPoint GetLastClientOffset() const { return mLastClientOffset; }
+
 protected:
 
   // returns the popup's level.
   nsPopupLevel PopupLevel(bool aIsNoAutoHide) const;
 
   // redefine to tell the box system not to move the views.
   virtual void GetLayoutFlags(PRUint32& aFlags);
 
@@ -430,16 +433,20 @@ protected:
   nsSize mPrefSize;
 
   // the position of the popup. The screen coordinates, if set to values other
   // than -1, override mXPos and mYPos.
   PRInt32 mXPos;
   PRInt32 mYPos;
   PRInt32 mScreenXPos;
   PRInt32 mScreenYPos;
+  // The value of the client offset of our widget the last time we positioned
+  // ourselves. We store this so that we can detect when it changes but the
+  // position of our widget didn't change.
+  nsIntPoint mLastClientOffset;
 
   nsPopupType mPopupType; // type of popup
   nsPopupState mPopupState; // open state of the popup
 
   // popup alignment relative to the anchor node
   PRInt8 mPopupAlignment;
   PRInt8 mPopupAnchor;
   // One of nsIPopupBoxObject::ROLLUP_DEFAULT/ROLLUP_CONSUME/ROLLUP_NO_CONSUME
--- a/layout/xul/base/src/nsResizerFrame.cpp
+++ b/layout/xul/base/src/nsResizerFrame.cpp
@@ -254,18 +254,17 @@ nsResizerFrame::HandleEvent(nsPresContex
           appUnitsRect.width = mRect.width;
         if (appUnitsRect.height < mRect.height && mouseMove.y)
           appUnitsRect.height = mRect.height;
         nsIntRect cssRect = appUnitsRect.ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
 
         nsIntRect oldRect;
         nsWeakFrame weakFrame(menuPopupFrame);
         if (menuPopupFrame) {
-          nsCOMPtr<nsIWidget> widget;
-          menuPopupFrame->GetWidget(getter_AddRefs(widget));
+          nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
           if (widget)
             widget->GetScreenBounds(oldRect);
 
           // convert the new rectangle into outer window coordinates
           nsIntPoint clientOffset = widget->GetClientOffset();
           rect.x -= clientOffset.x; 
           rect.y -= clientOffset.y; 
         }
--- a/layout/xul/base/src/nsTitleBarFrame.cpp
+++ b/layout/xul/base/src/nsTitleBarFrame.cpp
@@ -156,18 +156,17 @@ nsTitleBarFrame::HandleEvent(nsPresConte
          nsIFrame* parent = GetParent();
          while (parent && parent->GetType() != nsGkAtoms::menuPopupFrame)
            parent = parent->GetParent();
 
          // if the titlebar is in a popup, move the popup frame, otherwise
          // move the widget associated with the window
          if (parent) {
            nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent);
-           nsCOMPtr<nsIWidget> widget;
-           menuPopupFrame->GetWidget(getter_AddRefs(widget));
+           nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
            nsIntRect bounds;
            widget->GetScreenBounds(bounds);
            menuPopupFrame->MoveTo(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y, false);
          }
          else {
            nsIPresShell* presShell = aPresContext->PresShell();
            nsPIDOMWindow *window = presShell->GetDocument()->GetWindow();
            if (window) {
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -278,18 +278,17 @@ nsXULPopupManager::GetSubmenuWidgetChain
   // this method is used by the widget code to determine the list of popups
   // that are open. If a mouse click occurs outside one of these popups, the
   // panels will roll up. If the click is inside a popup, they will not roll up
   PRUint32 count = 0, sameTypeCount = 0;
 
   NS_ASSERTION(aWidgetChain, "null parameter");
   nsMenuChainItem* item = GetTopVisibleMenu();
   while (item) {
-    nsCOMPtr<nsIWidget> widget;
-    item->Frame()->GetWidget(getter_AddRefs(widget));
+    nsCOMPtr<nsIWidget> widget = item->Frame()->GetWidget();
     NS_ASSERTION(widget, "open popup has no widget");
     aWidgetChain->AppendElement(widget.get());
     // In the case when a menulist inside a panel is open, clicking in the
     // panel should still roll up the menu, so if a different type is found,
     // stop scanning.
     nsMenuChainItem* parent = item->GetParent();
     if (!sameTypeCount) {
       count++;
@@ -354,17 +353,19 @@ nsXULPopupManager::PopupMoved(nsIFrame* 
 {
   nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aFrame);
   if (!menuPopupFrame)
     return;
 
   // Don't do anything if the popup is already at the specified location. This
   // prevents recursive calls when a popup is positioned.
   nsIntPoint currentPnt = menuPopupFrame->ScreenPosition();
-  if (aPnt.x != currentPnt.x || aPnt.y != currentPnt.y) {
+  nsIWidget* widget = menuPopupFrame->GetWidget();
+  if ((aPnt.x != currentPnt.x || aPnt.y != currentPnt.y) || (widget &&
+      widget->GetClientOffset() != menuPopupFrame->GetLastClientOffset())) {
     // Update the popup's position using SetPopupPosition if the popup is
     // anchored and at the parent level as these maintain their position
     // relative to the parent window. Otherwise, just update the popup to
     // the specified screen coordinates.
     if (menuPopupFrame->IsAnchored() &&
         menuPopupFrame->PopupLevel() == ePopupLevelParent) {
       menuPopupFrame->SetPopupPosition(nsnull, true);
     }
@@ -1459,18 +1460,17 @@ nsXULPopupManager::MayShowPopup(nsMenuPo
 
   // Don't show popups that we already have in our popup chain
   if (IsPopupOpen(aPopup->GetContent())) {
     NS_WARNING("Refusing to show duplicate popup");
     return false;
   }
 
   // if the popup was just rolled up, don't reopen it
-  nsCOMPtr<nsIWidget> widget;
-  aPopup->GetWidget(getter_AddRefs(widget));
+  nsCOMPtr<nsIWidget> widget = aPopup->GetWidget();
   if (widget && widget->GetLastRollup() == aPopup->GetContent())
       return false;
 
   nsCOMPtr<nsISupports> cont = aPopup->PresContext()->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
   if (!baseWin)
     return false;
@@ -1613,18 +1613,17 @@ nsXULPopupManager::SetCaptureState(nsICo
 
   if (mWidget) {
     mWidget->CaptureRollupEvents(this, false, false);
     mWidget = nsnull;
   }
 
   if (item) {
     nsMenuPopupFrame* popup = item->Frame();
-    nsCOMPtr<nsIWidget> widget;
-    popup->GetWidget(getter_AddRefs(widget));
+    nsCOMPtr<nsIWidget> widget = popup->GetWidget();
     if (widget) {
       widget->CaptureRollupEvents(this, true, popup->ConsumeOutsideClicks());
       mWidget = widget;
       popup->AttachedDismissalListener();
     }
   }
 
   UpdateKeyboardListeners();
--- a/layout/xul/test/browser_bug703210.js
+++ b/layout/xul/test/browser_bug703210.js
@@ -2,17 +2,17 @@ function test() {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
 
   let doStopPropagation = function (aEvent)
   {
     aEvent.stopPropagation();
   }
 
-  let onPopupShowing = function (aEvent)
+  let onPopupShown = function (aEvent)
   {
     is(aEvent.originalTarget.localName, "tooltip", "tooltip is showing");
 
     let doc = gBrowser.contentDocument;
     let win = gBrowser.contentWindow;
     let p2 = doc.getElementById("p2");
     setTimeout(function () {
       EventUtils.synthesizeMouseAtCenter(p2, { type: "mousemove" }, win); }, 0);
@@ -24,17 +24,17 @@ function test() {
 
     let doc = gBrowser.contentDocument;
 
     doc.removeEventListener("mousemove", doStopPropagation, true);
     doc.removeEventListener("mouseenter", doStopPropagation, true);
     doc.removeEventListener("mouseleave", doStopPropagation, true);
     doc.removeEventListener("mouseover", doStopPropagation, true);
     doc.removeEventListener("mouseout", doStopPropagation, true);
-    document.removeEventListener("popupshowing", onPopupShowing, true);
+    document.removeEventListener("popupshown", onPopupShown, true);
     document.removeEventListener("popuphiding", onPopupHiding, true);
 
     gBrowser.removeCurrentTab();
     finish();
   }
 
   let onLoad = function (aEvent)
   {
@@ -45,17 +45,17 @@ function test() {
 
     EventUtils.synthesizeMouseAtCenter(p2, { type: "mousemove" }, win);
 
     doc.addEventListener("mousemove", doStopPropagation, true);
     doc.addEventListener("mouseenter", doStopPropagation, true);
     doc.addEventListener("mouseleave", doStopPropagation, true);
     doc.addEventListener("mouseover", doStopPropagation, true);
     doc.addEventListener("mouseout", doStopPropagation, true);
-    document.addEventListener("popupshown", onPopupShowing, true);
+    document.addEventListener("popupshown", onPopupShown, true);
     document.addEventListener("popuphiding", onPopupHiding, true);
 
     EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win);
   }
 
   gBrowser.selectedBrowser.addEventListener("load",
     function () { setTimeout(onLoad, 0); }, true);
 
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -70,20 +70,21 @@
 #      define MOZ_HAVE_CXX11_DELETE
 #    endif
 #  endif
 #elif defined(__GNUC__)
 #  if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
 #    if __GNUC__ > 4
 #      define MOZ_HAVE_CXX11_DELETE
 #      define MOZ_HAVE_CXX11_OVERRIDE
-#      define MOZ_HAVE CXX11_FINAL       final
+#      define MOZ_HAVE_CXX11_FINAL       final
 #    elif __GNUC__ == 4
 #      if __GNUC_MINOR__ >= 7
 #        define MOZ_HAVE_CXX11_OVERRIDE
+#        define MOZ_HAVE_CXX11_FINAL     final
 #      endif
 #      if __GNUC_MINOR__ >= 4
 #        define MOZ_HAVE_CXX11_DELETE
 #      endif
 #    endif
 #  else
      /* __final is a non-C++11 GCC synonym for 'final', per GCC r176655. */
 #    if __GNUC__ > 4
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -782,16 +782,23 @@ pref("network.http.qos", 0);
 // to wait before trying a different connection. 0 means do not use a second
 // connection.
 pref("network.http.connection-retry-timeout", 250);
 
 // Disable IPv6 for backup connections to workaround problems about broken
 // IPv6 connectivity.
 pref("network.http.fast-fallback-to-IPv4", true);
 
+// Try and use SPDY when using SSL
+pref("network.http.spdy.enabled", false);
+pref("network.http.spdy.chunk-size", 4096);
+pref("network.http.spdy.timeout", 180);
+pref("network.http.spdy.coalesce-hostnames", true);
+pref("network.http.spdy.use-alternate-protocol", true);
+
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
 // </http>
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -547,17 +547,18 @@ nsSocketOutputStream::Flush()
 
 NS_IMETHODIMP
 nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
 {
     SOCKET_LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count));
 
     *countWritten = 0;
 
-    if (count == 0)
+    // A write of 0 bytes can be used to force the initial SSL handshake.
+    if (count == 0 && mByteCount)
         return NS_OK;
 
     PRFileDesc *fd;
     {
         MutexAutoLock lock(mTransport->mLock);
 
         if (NS_FAILED(mCondition))
             return mCondition;
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -49,16 +49,24 @@
 #include "plstr.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIOService.h"
 
 #include "mozilla/FunctionTimer.h"
 
+// XXX: There is no good header file to put these in. :(
+namespace mozilla { namespace psm {
+
+void InitializeSSLServerCertVerificationThreads();
+void StopSSLServerCertVerificationThreads();
+
+} } // namespace mozilla::psm
+
 using namespace mozilla;
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gSocketTransportLog = nsnull;
 #endif
 
 nsSocketTransportService *gSocketTransportService = nsnull;
 PRThread                 *gSocketThread           = nsnull;
@@ -604,16 +612,18 @@ nsSocketTransportService::AfterProcessNe
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::Run()
 {
     SOCKET_LOG(("STS thread init\n"));
 
+    psm::InitializeSSLServerCertVerificationThreads();
+
     gSocketThread = PR_GetCurrentThread();
 
     // add thread event to poll list (mThreadEvent may be NULL)
     mPollList[0].fd = mThreadEvent;
     mPollList[0].in_flags = PR_POLL_READ;
     mPollList[0].out_flags = 0;
 
     nsIThread *thread = NS_GetCurrentThread();
@@ -660,16 +670,18 @@ nsSocketTransportService::Run()
         DetachSocket(mIdleList, &mIdleList[i]);
 
     // Final pass over the event queue. This makes sure that events posted by
     // socket detach handlers get processed.
     NS_ProcessPendingEvents(thread);
 
     gSocketThread = nsnull;
 
+    psm::StopSSLServerCertVerificationThreads();
+
     SOCKET_LOG(("STS thread exit\n"));
     return NS_OK;
 }
 
 nsresult
 nsSocketTransportService::DoPollIteration(bool wait)
 {
     SOCKET_LOG(("STS poll iter [%d]\n", wait));
--- a/netwerk/base/src/nsStandardURL.cpp
+++ b/netwerk/base/src/nsStandardURL.cpp
@@ -516,29 +516,30 @@ nsStandardURL::BuildNormalizedSpec(const
     // spec length.
     //
     // [scheme://][username[:password]@]host[:port]/path[?query_string][#ref]
 
     PRUint32 approxLen = 0;
 
     // the scheme is already ASCII
     if (mScheme.mLen > 0)
-        approxLen += mScheme.mLen + 3; // includes room for "://";
+        approxLen += mScheme.mLen + 3; // includes room for "://", which we insert always
 
     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
     // results written to encXXX variables only if |spec| is not already in the
     // appropriate encoding.
     {
         GET_SEGMENT_ENCODER(encoder);
         GET_QUERY_ENCODER(queryEncoder);
         // Items using an extraLen of 1 don't add anything unless mLen > 0
         // Username@
         approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername, 1);
-        // :Password
-        approxLen += encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword, 1);
+        // :password - we insert the ':' even if there's no actual password if "user:@" was in the spec
+        if (mPassword.mLen >= 0)
+            approxLen += 1 + encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
         // mHost is handled differently below due to encoding differences
         NS_ABORT_IF_FALSE(mPort > 0 || mPort == -1, "Invalid negative mPort");
         if (mPort != -1 && mPort != mDefaultPort)
         {
             // :port
             portbuf.AppendInt(mPort);
             approxLen += portbuf.Length() + 1;
         }
@@ -558,16 +559,17 @@ nsStandardURL::BuildNormalizedSpec(const
         if (mRef.mLen >= 0)
             approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef,       esc_Ref,           encRef,       useEncRef);
     }
 
     // do not escape the hostname, if IPv6 address literal, mHost will
     // already point to a [ ] delimited IPv6 address literal.
     // However, perform Unicode normalization on it, as IDN does.
     mHostEncoding = eEncoding_ASCII;
+    // Note that we don't disallow URLs without a host - file:, etc
     if (mHost.mLen > 0) {
         const nsCSubstring& tempHost =
             Substring(spec + mHost.mPos, spec + mHost.mPos + mHost.mLen);
         if (tempHost.FindChar('\0') != kNotFound)
             return NS_ERROR_MALFORMED_URI;  // null embedded in hostname
         if (tempHost.FindChar(' ') != kNotFound)
             return NS_ERROR_MALFORMED_URI;  // don't allow spaces in the hostname
         if ((useEncHost = NormalizeIDN(tempHost, encHost)))
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -692,17 +692,17 @@ nsCacheProfilePrefObserver::ReadPrefs(ns
                 bool same;
                 if (NS_SUCCEEDED(profDir->Equals(directory, &same)) && !same) {
                     // We no longer store the cache directory in the main
                     // profile directory, so we should cleanup the old one.
                     rv = profDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
                     if (NS_SUCCEEDED(rv)) {
                         bool exists;
                         if (NS_SUCCEEDED(profDir->Exists(&exists)) && exists)
-                            DeleteDir(profDir, false, false);
+                            nsDeleteDir::DeleteDir(profDir, false);
                     }
                 }
             }
         }
         // use file cache in build tree only if asked, to avoid cache dir litter
         if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
             rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
                                         getter_AddRefs(directory));
@@ -1033,16 +1033,21 @@ nsCacheService::Init()
 
     CACHE_LOG_INIT();
 
     nsresult rv = NS_NewThread(getter_AddRefs(mCacheIOThread));
     if (NS_FAILED(rv)) {
         NS_WARNING("Can't create cache IO thread");
     }
 
+    rv = nsDeleteDir::Init();
+    if (NS_FAILED(rv)) {
+        NS_WARNING("Can't initialize nsDeleteDir");
+    }
+
     // initialize hashtable for active cache entries
     rv = mActiveEntries.Init();
     if (NS_FAILED(rv)) return rv;
     
     // create profile/preference observer
     mObserver = new nsCacheProfilePrefObserver();
     if (!mObserver)  return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(mObserver);
@@ -1056,16 +1061,17 @@ nsCacheService::Init()
     return NS_OK;
 }
 
 
 void
 nsCacheService::Shutdown()
 {
     nsCOMPtr<nsIThread> cacheIOThread;
+    Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN> totalTimer;
 
     {
     nsCacheServiceAutoLock lock;
     NS_ASSERTION(mInitialized, 
                  "can't shutdown nsCacheService unless it has been initialized.");
 
     if (mInitialized) {
 
@@ -1101,16 +1107,39 @@ nsCacheService::Shutdown()
 #endif
 
         mCacheIOThread.swap(cacheIOThread);
     }
     } // lock
 
     if (cacheIOThread)
         cacheIOThread->Shutdown();
+
+    bool finishDeleting = false;
+    nsresult rv;
+    nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (!branch) {
+        NS_WARNING("Failed to get pref service!");
+    } else {
+        bool isSet;
+        rv = branch->GetBoolPref("privacy.sanitize.sanitizeOnShutdown", &isSet);
+        if (NS_SUCCEEDED(rv) && isSet) {
+            rv = branch->GetBoolPref("privacy.clearOnShutdown.cache", &isSet);
+            if (NS_SUCCEEDED(rv) && isSet) {
+                finishDeleting = true;
+            }
+        }
+    }
+    if (finishDeleting) {
+      Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE> timer;
+      nsDeleteDir::Shutdown(finishDeleting);
+    } else {
+      Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN> timer;
+      nsDeleteDir::Shutdown(finishDeleting);
+    }
 }
 
 
 nsresult
 nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
 {
     nsresult  rv;
 
--- a/netwerk/cache/nsDeleteDir.cpp
+++ b/netwerk/cache/nsDeleteDir.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
  *  Jason Duell <jduell.mcbugs@gmail.com>
+ *  Michal Novotny <michal.novotny@gmail.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
@@ -35,110 +36,427 @@
  * 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 "nsDeleteDir.h"
 #include "nsIFile.h"
 #include "nsString.h"
-#include "prthread.h"
 #include "mozilla/Telemetry.h"
 #include "nsITimer.h"
+#include "nsISimpleEnumerator.h"
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsISupportsPriority.h"
 
 using namespace mozilla;
 
-static void DeleteDirThreadFunc(void *arg)
+class nsBlockOnBackgroundThreadEvent : public nsRunnable {
+public:
+  nsBlockOnBackgroundThreadEvent() {}
+  NS_IMETHOD Run()
+  {
+    MutexAutoLock lock(nsDeleteDir::gInstance->mLock);
+    nsDeleteDir::gInstance->mCondVar.Notify();
+    return NS_OK;
+  }
+};
+
+class nsDestroyThreadEvent : public nsRunnable {
+public:
+  nsDestroyThreadEvent(nsIThread *thread)
+    : mThread(thread)
+  {}
+  NS_IMETHOD Run()
+  {
+    mThread->Shutdown();
+    return NS_OK;
+  }
+private:
+  nsCOMPtr<nsIThread> mThread;
+};
+
+
+nsDeleteDir * nsDeleteDir::gInstance = nsnull;
+
+nsDeleteDir::nsDeleteDir()
+  : mLock("nsDeleteDir.mLock"),
+    mCondVar(mLock, "nsDeleteDir.mCondVar"),
+    mShutdownPending(false),
+    mStopDeleting(false)
 {
-  Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR> timer;
-  nsIFile *dir = static_cast<nsIFile *>(arg);
-  dir->Remove(true);
-  NS_RELEASE(dir);
+  NS_ASSERTION(gInstance==nsnull, "multiple nsCacheService instances!");
+}
+
+nsDeleteDir::~nsDeleteDir()
+{
+  gInstance = nsnull;
+}
+
+nsresult
+nsDeleteDir::Init()
+{
+  if (gInstance)
+    return NS_ERROR_ALREADY_INITIALIZED;
+
+  gInstance = new nsDeleteDir();
+  return NS_OK;
 }
 
-static void CreateDeleterThread(nsITimer *aTimer, void *arg)
+nsresult
+nsDeleteDir::Shutdown(bool finishDeleting)
 {
-  nsIFile *dir = static_cast<nsIFile *>(arg);
+  if (!gInstance)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  nsCOMArray<nsIFile> dirsToRemove;
+  nsCOMPtr<nsIThread> thread;
+  {
+    MutexAutoLock lock(gInstance->mLock);
+    NS_ASSERTION(!gInstance->mShutdownPending,
+                 "Unexpected state in nsDeleteDir::Shutdown()");
+    gInstance->mShutdownPending = true;
+
+    if (!finishDeleting)
+      gInstance->mStopDeleting = true;
+
+    // remove all pending timers
+    for (PRInt32 i = gInstance->mTimers.Count(); i > 0; i--) {
+      nsCOMPtr<nsITimer> timer = gInstance->mTimers[i-1];
+      gInstance->mTimers.RemoveObjectAt(i-1);
+      timer->Cancel();
+
+      nsCOMArray<nsIFile> *arg;
+      timer->GetClosure((reinterpret_cast<void**>(&arg)));
 
-  // create the worker thread
-  PR_CreateThread(PR_USER_THREAD, DeleteDirThreadFunc, dir, PR_PRIORITY_LOW,
-                  PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+      if (finishDeleting)
+        dirsToRemove.AppendObjects(*arg);
+
+      // delete argument passed to the timer
+      delete arg;
+    }
+
+    thread.swap(gInstance->mThread);
+    if (thread) {
+      // dispatch event and wait for it to run and notify us, so we know thread
+      // has completed all work and can be shutdown
+      nsCOMPtr<nsIRunnable> event = new nsBlockOnBackgroundThreadEvent();
+      nsresult rv = thread->Dispatch(event, NS_DISPATCH_NORMAL);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed dispatching block-event");
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      rv = gInstance->mCondVar.Wait();
+      thread->Shutdown();
+    }
+  }
+
+  delete gInstance;
+
+  for (PRInt32 i = 0; i < dirsToRemove.Count(); i++)
+    dirsToRemove[i]->Remove(true);
+
+  return NS_OK;
 }
 
-nsresult DeleteDir(nsIFile *dirIn, bool moveToTrash, bool sync,
-                   PRUint32 delay)
+nsresult
+nsDeleteDir::InitThread()
+{
+  if (mThread)
+    return NS_OK;
+
+  nsresult rv = NS_NewThread(getter_AddRefs(mThread));
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Can't create background thread");
+    return rv;
+  }
+
+  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mThread);
+  if (p) {
+    p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+  }
+  return NS_OK;
+}
+
+void
+nsDeleteDir::DestroyThread()
+{
+  if (!mThread)
+    return;
+
+  if (mTimers.Count())
+    // more work to do, so don't delete thread.
+    return;
+
+  NS_DispatchToMainThread(new nsDestroyThreadEvent(mThread));
+  mThread = nsnull;
+}
+
+void
+nsDeleteDir::TimerCallback(nsITimer *aTimer, void *arg)
+{
+  Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_DELETEDIR> timer;
+  {
+    MutexAutoLock lock(gInstance->mLock);
+
+    PRInt32 idx = gInstance->mTimers.IndexOf(aTimer);
+    if (idx == -1) {
+      // Timer was canceled and removed during shutdown.
+      return;
+    }
+
+    gInstance->mTimers.RemoveObjectAt(idx);
+  }
+
+  nsAutoPtr<nsCOMArray<nsIFile> > dirList;
+  dirList = static_cast<nsCOMArray<nsIFile> *>(arg);
+
+  bool shuttingDown = false;
+  for (PRInt32 i = 0; i < dirList->Count() && !shuttingDown; i++) {
+    gInstance->RemoveDir((*dirList)[i], &shuttingDown);
+  }
+
+  {
+    MutexAutoLock lock(gInstance->mLock);
+    gInstance->DestroyThread();
+  }
+}
+
+nsresult
+nsDeleteDir::DeleteDir(nsIFile *dirIn, bool moveToTrash, PRUint32 delay)
 {
   Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_TRASHRENAME> timer;
+
+  if (!gInstance)
+    return NS_ERROR_NOT_INITIALIZED;
+
   nsresult rv;
   nsCOMPtr<nsIFile> trash, dir;
 
   // Need to make a clone of this since we don't want to modify the input
   // file object.
   rv = dirIn->Clone(getter_AddRefs(dir));
   if (NS_FAILED(rv))
     return rv;
 
   if (moveToTrash) {
     rv = GetTrashDir(dir, &trash);
     if (NS_FAILED(rv))
       return rv;
-    nsCAutoString leaf;
-    rv = trash->GetNativeLeafName(leaf);
+    nsCAutoString origLeaf;
+    rv = trash->GetNativeLeafName(origLeaf);
     if (NS_FAILED(rv))
       return rv;
 
     // Important: must rename directory w/o changing parent directory: else on
     // NTFS we'll wait (with cache lock) while nsIFile's ACL reset walks file
     // tree: was hanging GUI for *minutes* on large cache dirs.
+    // Append random number to the trash directory and check if it exists.
+    nsCAutoString leaf;
+    for (PRInt32 i = 0; i < 10; i++) {
+      leaf = origLeaf;
+      leaf.AppendInt(rand());
+      rv = trash->SetNativeLeafName(leaf);
+      if (NS_FAILED(rv))
+        return rv;
+
+      bool exists;
+      if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
+        break;
+      }
+
+      leaf.Truncate();
+    }
+
+    // Fail if we didn't find unused trash directory within the limit
+    if (!leaf.Length())
+      return NS_ERROR_FAILURE;
+
     rv = dir->MoveToNative(nsnull, leaf);
-    if (NS_FAILED(rv)) {
-      nsresult rvMove = rv;
-      // TrashDir may already exist (if we crashed while deleting it, etc.)
-      // In that case current Cache dir should be small--just move it to
-      // subdirectory of TrashDir and eat the NTFS ACL overhead.
-      leaf.AppendInt(rand()); // support this happening multiple times
-      rv = dir->MoveToNative(trash, leaf);
-      if (NS_FAILED(rv))
-        return rvMove;
-      // Be paranoid and delete immediately if we're seeing old trash when
-      // we're creating a new one
-      delay = 0;
-    }
+    if (NS_FAILED(rv))
+      return rv;
   } else {
     // we want to pass a clone of the original off to the worker thread.
     trash.swap(dir);
   }
 
-  // Steal ownership of trash directory; let the thread release it.
-  nsIFile *trashRef = nsnull;
-  trash.swap(trashRef);
+  nsAutoPtr<nsCOMArray<nsIFile> > arg(new nsCOMArray<nsIFile>);
+  arg->AppendObject(trash);
 
-  if (sync) {
-    DeleteDirThreadFunc(trashRef);
-  } else {
-    if (delay) {
-      nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-      if (NS_FAILED(rv))
-        return NS_ERROR_UNEXPECTED;
-      timer->InitWithFuncCallback(CreateDeleterThread, trashRef, delay,
-                                  nsITimer::TYPE_ONE_SHOT);
-    } else {
-      CreateDeleterThread(nsnull, trashRef);
-    }
-  }
+  rv = gInstance->PostTimer(arg, delay);
+  if (NS_FAILED(rv))
+    return rv;
 
+  arg.forget();
   return NS_OK;
 }
 
-nsresult GetTrashDir(nsIFile *target, nsCOMPtr<nsIFile> *result)
+nsresult
+nsDeleteDir::GetTrashDir(nsIFile *target, nsCOMPtr<nsIFile> *result)
 {
   nsresult rv = target->Clone(getter_AddRefs(*result));
   if (NS_FAILED(rv))
     return rv;
 
   nsCAutoString leaf;
   rv = (*result)->GetNativeLeafName(leaf);
   if (NS_FAILED(rv))
     return rv;
   leaf.AppendLiteral(".Trash");
 
   return (*result)->SetNativeLeafName(leaf);
 }
+
+nsresult
+nsDeleteDir::RemoveOldTrashes(nsIFile *cacheDir)
+{
+  if (!gInstance)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  nsresult rv;
+
+  static bool firstRun = true;
+
+  if (!firstRun)
+    return NS_OK;
+
+  firstRun = false;
+
+  nsCOMPtr<nsIFile> trash;
+  rv = GetTrashDir(cacheDir, &trash);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsAutoString trashName;
+  rv = trash->GetLeafName(trashName);
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCOMPtr<nsIFile> parent;
+  rv = cacheDir->GetParent(getter_AddRefs(parent));
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCOMPtr<nsISimpleEnumerator> iter;
+  rv = parent->GetDirectoryEntries(getter_AddRefs(iter));
+  if (NS_FAILED(rv))
+    return rv;
+
+  bool more;
+  nsCOMPtr<nsISupports> elem;
+  nsAutoPtr<nsCOMArray<nsIFile> > dirList;
+
+  while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
+    rv = iter->GetNext(getter_AddRefs(elem));
+    if (NS_FAILED(rv))
+      continue;
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
+    if (!file)
+      continue;
+
+    nsAutoString leafName;
+    rv = file->GetLeafName(leafName);
+    if (NS_FAILED(rv))
+      continue;
+
+    // match all names that begin with the trash name (i.e. "Cache.Trash")
+    if (Substring(leafName, 0, trashName.Length()).Equals(trashName)) {
+      if (!dirList)
+        dirList = new nsCOMArray<nsIFile>;
+      dirList->AppendObject(file);
+    }
+  }
+
+  if (dirList) {
+    rv = gInstance->PostTimer(dirList, 90000);
+    if (NS_FAILED(rv))
+      return rv;
+
+    dirList.forget();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsDeleteDir::PostTimer(void *arg, PRUint32 delay)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+  if (NS_FAILED(rv))
+    return NS_ERROR_UNEXPECTED;
+
+  MutexAutoLock lock(mLock);
+
+  rv = InitThread();
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = timer->SetTarget(mThread);
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = timer->InitWithFuncCallback(TimerCallback, arg, delay,
+                                   nsITimer::TYPE_ONE_SHOT);
+  if (NS_FAILED(rv))
+    return rv;
+
+  mTimers.AppendObject(timer);
+  return NS_OK;
+}
+
+nsresult
+nsDeleteDir::RemoveDir(nsIFile *file, bool *stopDeleting)
+{
+  nsresult rv;
+  bool isLink;
+
+  rv = file->IsSymlink(&isLink);
+  if (NS_FAILED(rv) || isLink)
+    return NS_ERROR_UNEXPECTED;
+
+  bool isDir;
+  rv = file->IsDirectory(&isDir);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (isDir) {
+    nsCOMPtr<nsISimpleEnumerator> iter;
+    rv = file->GetDirectoryEntries(getter_AddRefs(iter));
+    if (NS_FAILED(rv))
+      return rv;
+
+    bool more;
+    nsCOMPtr<nsISupports> elem;
+    while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
+      rv = iter->GetNext(getter_AddRefs(elem));
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Unexpected failure in nsDeleteDir::RemoveDir");
+        continue;
+      }
+
+      nsCOMPtr<nsIFile> file2 = do_QueryInterface(elem);
+      if (!file2) {
+        NS_WARNING("Unexpected failure in nsDeleteDir::RemoveDir");
+        continue;
+      }
+
+      RemoveDir(file2, stopDeleting);
+      // No check for errors to remove as much as possible
+
+      if (*stopDeleting)
+        return NS_OK;
+    }
+  }
+
+  file->Remove(false);
+  // No check for errors to remove as much as possible
+
+  MutexAutoLock lock(mLock);
+  if (mStopDeleting)
+    *stopDeleting = true;
+
+  return NS_OK;
+}
--- a/netwerk/cache/nsDeleteDir.h
+++ b/netwerk/cache/nsDeleteDir.h
@@ -16,16 +16,17 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
+ *  Michal Novotny <michal.novotny@gmail.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
@@ -35,44 +36,77 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDeleteDir_h__
 #define nsDeleteDir_h__
 
 #include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
 
 class nsIFile;
+class nsIThread;
+class nsITimer;
 
-/**
- * This routine attempts to delete a directory that may contain some files that
- * are still in use.  This later point is only an issue on Windows and a few
- * other systems.
- *
- * If the moveToTrash parameter is true, then the process for deleting the
- * directory creates a sibling directory of the same name with the ".Trash"
- * suffix.  It then attempts to move the given directory into the corresponding
- * trash folder (moving individual files if necessary).  Next, it proceeds to
- * delete each file in the trash folder on a low-priority background thread.
- *
- * If the moveToTrash parameter is false, then the given directory is deleted
- * directly.
- *
- * If the sync flag is true, then the delete operation runs to completion
- * before this function returns.  Otherwise, deletion occurs asynchronously.
- *
- * If 'delay' is non-zero, the directory will not be deleted until the
- * specified number of milliseconds have passed. (The directory is still
- * renamed immediately if 'moveToTrash' is passed, so upon return it is safe
- * to create a directory with the same name). This parameter is ignored if
- * 'sync' is true.
- */
-NS_HIDDEN_(nsresult) DeleteDir(nsIFile *dir, bool moveToTrash, bool sync, 
-                               PRUint32 delay = 0);
+
+class nsDeleteDir {
+public:
+  nsDeleteDir();
+  ~nsDeleteDir();
+
+  static nsresult Init();
+  static nsresult Shutdown(bool finishDeleting);
+
+  /**
+   * This routine attempts to delete a directory that may contain some files
+   * that are still in use. This latter point is only an issue on Windows and
+   * a few other systems.
+   *
+   * If the moveToTrash parameter is true we first rename the given directory
+   * "foo.Trash123" (where "foo" is the original directory name, and "123" is
+   * a random number, in order to support multiple concurrent deletes). The
+   * directory is then deleted, file-by-file, on a background thread.
+   *
+   * If the moveToTrash parameter is false, then the given directory is deleted
+   * directly.
+   *
+   * If 'delay' is non-zero, the directory will not be deleted until the
+   * specified number of milliseconds have passed. (The directory is still
+   * renamed immediately if 'moveToTrash' is passed, so upon return it is safe
+   * to create a directory with the same name).
+   */
+  static nsresult DeleteDir(nsIFile *dir, bool moveToTrash, PRUint32 delay = 0);
 
-/**
- * This routine returns the trash directory corresponding to the given 
- * directory.
- */
-NS_HIDDEN_(nsresult) GetTrashDir(nsIFile *dir, nsCOMPtr<nsIFile> *result);
+  /**
+   * Returns the trash directory corresponding to the given directory.
+   */
+  static nsresult GetTrashDir(nsIFile *dir, nsCOMPtr<nsIFile> *result);
+
+  /**
+   * Remove all trashes left from previous run. This function does nothing when
+   * called second and more times.
+   */
+  static nsresult RemoveOldTrashes(nsIFile *cacheDir);
+
+  static void TimerCallback(nsITimer *aTimer, void *arg);
+
+private:
+  friend class nsBlockOnBackgroundThreadEvent;
+  friend class nsDestroyThreadEvent;
+
+  nsresult InitThread();
+  void     DestroyThread();
+  nsresult PostTimer(void *arg, PRUint32 delay);
+  nsresult RemoveDir(nsIFile *file, bool *stopDeleting);
+
+  static nsDeleteDir * gInstance;
+  mozilla::Mutex       mLock;
+  mozilla::CondVar     mCondVar;
+  nsCOMArray<nsITimer> mTimers;
+  nsCOMPtr<nsIThread>  mThread;
+  bool                 mShutdownPending;
+  bool                 mStopDeleting;
+};
 
 #endif  // nsDeleteDir_h__
--- a/netwerk/cache/nsDiskCacheDevice.cpp
+++ b/netwerk/cache/nsDiskCacheDevice.cpp
@@ -414,17 +414,19 @@ nsDiskCacheDevice::Init()
     }
        
     if (!mCacheDirectory)
         return NS_ERROR_FAILURE;
 
     rv = mBindery.Init();
     if (NS_FAILED(rv))
         return rv;
-    
+
+    nsDeleteDir::RemoveOldTrashes(mCacheDirectory);
+
     // Open Disk Cache
     rv = OpenDiskCache();
     if (NS_FAILED(rv)) {
         (void) mCacheMap.Close(false);
         return rv;
     }
 
     mInitialized = true;
@@ -439,27 +441,16 @@ nsresult
 nsDiskCacheDevice::Shutdown()
 {
     nsCacheService::AssertOwnsLock();
 
     nsresult rv = Shutdown_Private(true);
     if (NS_FAILED(rv))
         return rv;
 
-    if (mCacheDirectory) {
-        // delete any trash files left-over before shutting down.
-        nsCOMPtr<nsIFile> trashDir;
-        GetTrashDir(mCacheDirectory, &trashDir);
-        if (trashDir) {
-            bool exists;
-            if (NS_SUCCEEDED(trashDir->Exists(&exists)) && exists)
-                DeleteDir(trashDir, false, true);
-        }
-    }
-
     return NS_OK;
 }
 
 
 nsresult
 nsDiskCacheDevice::Shutdown_Private(bool    flush)
 {
     CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush));
@@ -999,28 +990,26 @@ nsDiskCacheDevice::OpenDiskCache()
 {
     Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_OPEN> timer;
     // if we don't have a cache directory, create one and open it
     bool exists;
     nsresult rv = mCacheDirectory->Exists(&exists);
     if (NS_FAILED(rv))
         return rv;
 
-    bool trashing = false;
     if (exists) {
         // Try opening cache map file.
         rv = mCacheMap.Open(mCacheDirectory);        
         // move "corrupt" caches to trash
         if (rv == NS_ERROR_FILE_CORRUPTED) {
             // delay delete by 1 minute to avoid IO thrash at startup
-            rv = DeleteDir(mCacheDirectory, true, false, 60000);
+            rv = nsDeleteDir::DeleteDir(mCacheDirectory, true, 60000);
             if (NS_FAILED(rv))
                 return rv;
             exists = false;
-            trashing = true;
         }
         else if (NS_FAILED(rv))
             return rv;
     }
 
     // if we don't have a cache directory, create one and open it
     if (!exists) {
         rv = mCacheDirectory->Create(nsIFile::DIRECTORY_TYPE, 0777);
@@ -1030,46 +1019,33 @@ nsDiskCacheDevice::OpenDiskCache()
             return rv;
     
         // reopen the cache map     
         rv = mCacheMap.Open(mCacheDirectory);
         if (NS_FAILED(rv))
             return rv;
     }
 
-    if (!trashing) {
-        // delete any trash files leftover from a previous run
-        nsCOMPtr<nsIFile> trashDir;
-        GetTrashDir(mCacheDirectory, &trashDir);
-        if (trashDir) {
-            bool exists;
-            if (NS_SUCCEEDED(trashDir->Exists(&exists)) && exists) {
-                // be paranoid and delete immediately if leftover
-                DeleteDir(trashDir, false, false);
-            }
-        }
-    }
-
     return NS_OK;
 }
 
 
 nsresult
 nsDiskCacheDevice::ClearDiskCache()
 {
     if (mBindery.ActiveBindings())
         return NS_ERROR_CACHE_IN_USE;
 
     nsresult rv = Shutdown_Private(false);  // false: don't bother flushing
     if (NS_FAILED(rv))
         return rv;
 
     // If the disk cache directory is already gone, then it's not an error if
     // we fail to delete it ;-)
-    rv = DeleteDir(mCacheDirectory, true, false);
+    rv = nsDeleteDir::DeleteDir(mCacheDirectory, true);
     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
         return rv;
 
     return Init();
 }
 
 
 nsresult
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -75,16 +75,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mForceAllowThirdPartyCookie(false)
   , mUploadStreamHasHeaders(false)
   , mInheritApplicationCache(true)
   , mChooseApplicationCache(false)
   , mLoadedFromApplicationCache(false)
   , mChannelIsForDownload(false)
   , mTracingEnabled(true)
   , mTimingEnabled(false)
+  , mAllowSpdy(true)
   , mSuspendCount(0)
   , mRedirectedCachekeys(nsnull)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
 
@@ -1305,16 +1306,32 @@ HttpBaseChannel::HTTPUpgrade(const nsACS
     NS_ENSURE_ARG(!aProtocolName.IsEmpty());
     NS_ENSURE_ARG_POINTER(aListener);
     
     mUpgradeProtocol = aProtocolName;
     mUpgradeProtocolCallback = aListener;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::GetAllowSpdy(bool *aAllowSpdy)
+{
+  NS_ENSURE_ARG_POINTER(aAllowSpdy);
+
+  *aAllowSpdy = mAllowSpdy;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy)
+{
+  mAllowSpdy = aAllowSpdy;
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsISupportsPriority
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetPriority(PRInt32 *value)
 {
   *value = mPriority;
@@ -1614,16 +1631,18 @@ HttpBaseChannel::SetupReplacementChannel
   httpChannel->SetAllowPipelining(mAllowPipelining);
   // convey the new redirection limit
   httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
 
   nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   if (httpInternal) {
     // convey the mForceAllowThirdPartyCookie flag
     httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie);
+    // convey the spdy flag
+    httpInternal->SetAllowSpdy(mAllowSpdy);
 
     // update the DocumentURI indicator since we are being redirected.
     // if this was a top-level document channel, then the new channel
     // should have its mDocumentURI point to newURI; otherwise, we
     // just need to pass along our mDocumentURI to the new channel.
     if (newURI && (mURI == mDocumentURI))
       httpInternal->SetDocumentURI(newURI);
     else
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -162,16 +162,19 @@ public:
   NS_IMETHOD GetCanceled(bool *aCanceled);
   NS_IMETHOD GetChannelIsForDownload(bool *aChannelIsForDownload);
   NS_IMETHOD SetChannelIsForDownload(bool aChannelIsForDownload);
   NS_IMETHOD SetCacheKeysRedirectChain(nsTArray<nsCString> *cacheKeys);
   NS_IMETHOD GetLocalAddress(nsACString& addr);
   NS_IMETHOD GetLocalPort(PRInt32* port);
   NS_IMETHOD GetRemoteAddress(nsACString& addr);
   NS_IMETHOD GetRemotePort(PRInt32* port);
+  NS_IMETHOD GetAllowSpdy(bool *aAllowSpdy);
+  NS_IMETHOD SetAllowSpdy(bool aAllowSpdy);
+  
   inline void CleanRedirectCacheChainIfNecessary()
   {
       if (mRedirectedCachekeys) {
           delete mRedirectedCachekeys;
           mRedirectedCachekeys = nsnull;
       }
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
@@ -290,16 +293,17 @@ protected:
   PRUint32                          mUploadStreamHasHeaders     : 1;
   PRUint32                          mInheritApplicationCache    : 1;
   PRUint32                          mChooseApplicationCache     : 1;
   PRUint32                          mLoadedFromApplicationCache : 1;
   PRUint32                          mChannelIsForDownload       : 1;
   PRUint32                          mTracingEnabled             : 1;
   // True if timing collection is enabled
   PRUint32                          mTimingEnabled              : 1;
+  PRUint32                          mAllowSpdy                  : 1;
 
   // Current suspension depth for this channel object
   PRUint32                          mSuspendCount;
 
   nsTArray<nsCString>              *mRedirectedCachekeys;
 };
 
 // Share some code while working around C++'s absurd inability to handle casting
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1074,17 +1074,17 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
 
   SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI),
                 IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags,
                 mRequestHeaders, mRequestHead.Method(),
                 IPC::InputStream(mUploadStream), mUploadStreamHasHeaders,
                 mPriority, mRedirectionLimit, mAllowPipelining,
                 mForceAllowThirdPartyCookie, mSendResumeAt,
                 mStartPos, mEntityID, mChooseApplicationCache, 
-                appCacheClientId);
+                appCacheClientId, mAllowSpdy);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -138,17 +138,18 @@ HttpChannelParent::RecvAsyncOpen(const I
                                  const PRUint16&            priority,
                                  const PRUint8&             redirectionLimit,
                                  const bool&              allowPipelining,
                                  const bool&              forceAllowThirdPartyCookie,
                                  const bool&                doResumeAt,
                                  const PRUint64&            startPos,
                                  const nsCString&           entityID,
                                  const bool&                chooseApplicationCache,
-                                 const nsCString&           appCacheClientID)
+                                 const nsCString&           appCacheClientID,
+                                 const bool&                allowSpdy)
 {
   nsCOMPtr<nsIURI> uri(aURI);
   nsCOMPtr<nsIURI> originalUri(aOriginalURI);
   nsCOMPtr<nsIURI> docUri(aDocURI);
   nsCOMPtr<nsIURI> referrerUri(aReferrerURI);
 
   nsCString uriSpec;
   uri->GetSpec(uriSpec);
@@ -198,16 +199,17 @@ HttpChannelParent::RecvAsyncOpen(const I
     httpChan->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL)
     httpChan->SetPriority(priority);
   httpChan->SetRedirectionLimit(redirectionLimit);
   httpChan->SetAllowPipelining(allowPipelining);
   httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
+  httpChan->SetAllowSpdy(allowSpdy);
 
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
     do_QueryInterface(mChannel);
   nsCOMPtr<nsIApplicationCacheService> appCacheService =
     do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
 
   bool setChooseApplicationCache = chooseApplicationCache;
   if (appCacheChan && appCacheService) {
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -92,17 +92,18 @@ protected:
                              const PRUint16&            priority,
                              const PRUint8&             redirectionLimit,
                              const bool&              allowPipelining,
                              const bool&              forceAllowThirdPartyCookie,
                              const bool&                doResumeAt,
                              const PRUint64&            startPos,
                              const nsCString&           entityID,
                              const bool&                chooseApplicationCache,
-                             const nsCString&           appCacheClientID);
+                             const nsCString&           appCacheClientID,
+                             const bool&                allowSpdy);
 
   virtual bool RecvConnectChannel(const PRUint32& channelId);
   virtual bool RecvSetPriority(const PRUint16& priority);
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset);
   virtual bool RecvSuspend();
   virtual bool RecvResume();
   virtual bool RecvCancel(const nsresult& status);
   virtual bool RecvRedirect2Verify(const nsresult& result,
--- a/netwerk/protocol/http/Makefile.in
+++ b/netwerk/protocol/http/Makefile.in
@@ -105,16 +105,18 @@ CPPSRCS = \
   HttpBaseChannel.cpp \
   nsHttpChannel.cpp \
   nsHttpPipeline.cpp \
   nsHttpActivityDistributor.cpp \
   nsHttpChannelAuthProvider.cpp \
   HttpChannelParent.cpp \
   HttpChannelChild.cpp \
   HttpChannelParentListener.cpp \
+  SpdySession.cpp \
+  SpdyStream.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(srcdir)/../../base/src \
   -I$(topsrcdir)/xpcom/ds \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
   $(NULL)
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -76,17 +76,18 @@ parent:
             PRUint16            priority,
             PRUint8             redirectionLimit,
             bool                allowPipelining,
             bool                forceAllowThirdPartyCookie,
             bool                resumeAt,
             PRUint64            startPos,
             nsCString           entityID,
             bool                chooseApplicationCache,
-            nsCString           appCacheClientID);
+            nsCString           appCacheClientID,
+            bool                allowSpdy);
 
   // Used to connect redirected-to channel on the parent with redirected-to
   // channel on the child.
   ConnectChannel(PRUint32 channelId);
 
   SetPriority(PRUint16 priority);
 
   SetCacheTokenCachedCharset(nsCString charset);
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/SpdySession.cpp
@@ -0,0 +1,1861 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "nsHttp.h"
+#include "SpdySession.h"
+#include "SpdyStream.h"
+#include "nsHttpConnection.h"
+#include "prnetdb.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Preferences.h"
+#include "prprf.h"
+
+#ifdef DEBUG
+// defined by the socket transport service while active
+extern PRThread *gSocketThread;
+#endif
+
+namespace mozilla {
+namespace net {
+
+// SpdySession has multiple inheritance of things that implement
+// nsISupports, so this magic is taken from nsHttpPipeline that
+// implements some of the same abstract classes.
+NS_IMPL_THREADSAFE_ADDREF(SpdySession)
+NS_IMPL_THREADSAFE_RELEASE(SpdySession)
+NS_INTERFACE_MAP_BEGIN(SpdySession)
+    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
+NS_INTERFACE_MAP_END
+
+SpdySession::SpdySession(nsAHttpTransaction *aHttpTransaction,
+                         nsISocketTransport *aSocketTransport,
+                         PRInt32 firstPriority)
+  : mSocketTransport(aSocketTransport),
+    mSegmentReader(nsnull),
+    mSegmentWriter(nsnull),
+    mSendingChunkSize(kSendingChunkSize),
+    mNextStreamID(1),
+    mConcurrentHighWater(0),
+    mDownstreamState(BUFFERING_FRAME_HEADER),
+    mPartialFrame(nsnull),
+    mFrameBufferSize(kDefaultBufferSize),
+    mFrameBufferUsed(0),
+    mFrameDataLast(false),
+    mFrameDataStream(nsnull),
+    mNeedsCleanup(nsnull),
+    mDecompressBufferSize(kDefaultBufferSize),
+    mDecompressBufferUsed(0),
+    mShouldGoAway(false),
+    mClosed(false),
+    mCleanShutdown(false),
+    mGoAwayID(0),
+    mMaxConcurrent(kDefaultMaxConcurrent),
+    mConcurrent(0),
+    mServerPushedResources(0),
+    mOutputQueueSize(kDefaultQueueSize),
+    mOutputQueueUsed(0),
+    mOutputQueueSent(0)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  LOG3(("SpdySession::SpdySession %p transaction 1 = %p",
+        this, aHttpTransaction));
+  
+  mStreamIDHash.Init();
+  mStreamTransactionHash.Init();
+  mConnection = aHttpTransaction->Connection();
+  mFrameBuffer = new char[mFrameBufferSize];
+  mDecompressBuffer = new char[mDecompressBufferSize];
+  mOutputQueueBuffer = new char[mOutputQueueSize];
+  zlibInit();
+  
+  mSendingChunkSize =
+    Preferences::GetInt("network.http.spdy.chunk-size", kSendingChunkSize);
+  AddStream(aHttpTransaction, firstPriority);
+}
+
+PLDHashOperator
+SpdySession::Shutdown(nsAHttpTransaction *key,
+                      nsAutoPtr<SpdyStream> &stream,
+                      void *closure)
+{
+  SpdySession *self = static_cast<SpdySession *>(closure);
+  
+  if (self->mCleanShutdown &&
+      self->mGoAwayID < stream->StreamID())
+    stream->Close(NS_ERROR_NET_RESET); // can be restarted
+  else
+    stream->Close(NS_ERROR_ABORT);
+
+  return PL_DHASH_NEXT;
+}
+
+SpdySession::~SpdySession()
+{
+  LOG3(("SpdySession::~SpdySession %p", this));
+
+  inflateEnd(&mDownstreamZlib);
+  deflateEnd(&mUpstreamZlib);
+  
+  mStreamTransactionHash.Enumerate(Shutdown, this);
+  Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
+  Telemetry::Accumulate(Telemetry::SPDY_TOTAL_STREAMS, (mNextStreamID - 1) / 2);
+  Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
+                        mServerPushedResources);
+}
+
+void
+SpdySession::LogIO(SpdySession *self, SpdyStream *stream, const char *label,
+                   const char *data, PRUint32 datalen)
+{
+  if (!LOG4_ENABLED())
+    return;
+  
+  LOG4(("SpdySession::LogIO %p stream=%p id=0x%X [%s]",
+        self, stream, stream ? stream->StreamID() : 0, label));
+
+  // Max line is (16 * 3) + 10(prefix) + newline + null
+  char linebuf[128];
+  PRUint32 index;
+  char *line = linebuf;
+
+  linebuf[127] = 0;
+
+  for (index = 0; index < datalen; ++index) {
+    if (!(index % 16)) {
+      if (index) {
+        *line = 0;
+        LOG4(("%s", linebuf));
+      }
+      line = linebuf;
+      PR_snprintf(line, 128, "%08X: ", index);
+      line += 10;
+    }
+    PR_snprintf(line, 128 - (line - linebuf), "%02X ",
+                ((unsigned char *)data)[index]);
+    line += 3;
+  }
+  if (index) {
+    *line = 0;
+    LOG4(("%s", linebuf));
+  }
+}
+
+typedef nsresult  (*Control_FX) (SpdySession *self);
+static Control_FX sControlFunctions[] = 
+{
+  nsnull,
+  SpdySession::HandleSynStream,
+  SpdySession::HandleSynReply,
+  SpdySession::HandleRstStream,
+  SpdySession::HandleSettings,
+  SpdySession::HandleNoop,
+  SpdySession::HandlePing,
+  SpdySession::HandleGoAway,
+  SpdySession::HandleHeaders,
+  SpdySession::HandleWindowUpdate
+};
+
+bool
+SpdySession::RoomForMoreConcurrent()
+{
+  return (mConcurrent < mMaxConcurrent);
+}
+
+bool
+SpdySession::RoomForMoreStreams()
+{
+  if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
+    return false;
+
+  return !mShouldGoAway;
+}
+
+PRUint32
+SpdySession::RegisterStreamID(SpdyStream *stream)
+{
+  LOG3(("SpdySession::RegisterStreamID session=%p stream=%p id=0x%X "
+        "concurrent=%d",this, stream, mNextStreamID, mConcurrent));
+
+  NS_ABORT_IF_FALSE(mNextStreamID < 0xfffffff0,
+                    "should have stopped admitting streams");
+  
+  PRUint32 result = mNextStreamID;
+  mNextStreamID += 2;
+
+  // We've used up plenty of ID's on this session. Start
+  // moving to a new one before there is a crunch involving
+  // server push streams or concurrent non-registered submits
+  if (mNextStreamID >= kMaxStreamID)
+    mShouldGoAway = true;
+
+  mStreamIDHash.Put(result, stream);
+  return result;
+}
+
+bool
+SpdySession::AddStream(nsAHttpTransaction *aHttpTransaction,
+                       PRInt32 aPriority)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(!mStreamTransactionHash.Get(aHttpTransaction),
+                    "AddStream duplicate transaction pointer");
+
+  aHttpTransaction->SetConnection(this);
+  SpdyStream *stream = new SpdyStream(aHttpTransaction,
+                                      this,
+                                      mSocketTransport,
+                                      mSendingChunkSize,
+                                      &mUpstreamZlib,
+                                      aPriority);
+
+  
+  LOG3(("SpdySession::AddStream session=%p stream=%p NextID=0x%X (tentative)",
+        this, stream, mNextStreamID));
+
+  mStreamTransactionHash.Put(aHttpTransaction, stream);
+
+  if (RoomForMoreConcurrent()) {
+    LOG3(("SpdySession::AddStream %p stream %p activated immediately.",
+          this, stream));
+    ActivateStream(stream);
+  }
+  else {
+    LOG3(("SpdySession::AddStream %p stream %p queued.",
+          this, stream));
+    mQueuedStreams.Push(stream);
+  }
+  
+  return true;
+}
+
+void
+SpdySession::ActivateStream(SpdyStream *stream)
+{
+  mConcurrent++;
+  if (mConcurrent > mConcurrentHighWater)
+    mConcurrentHighWater = mConcurrent;
+  LOG3(("SpdySession::AddStream %p activating stream %p Currently %d"
+        "streams in session, high water mark is %d",
+        this, stream, mConcurrent, mConcurrentHighWater));
+
+  mReadyForWrite.Push(stream);
+  SetWriteCallbacks(stream->Transaction());
+
+  // Kick off the SYN transmit without waiting for the poll loop
+  PRUint32 countRead;
+  ReadSegments(nsnull, kDefaultBufferSize, &countRead);
+}
+
+void
+SpdySession::ProcessPending()
+{
+  while (RoomForMoreConcurrent()) {
+    SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
+    if (!stream)
+      return;
+    LOG3(("SpdySession::ProcessPending %p stream %p activated from queue.",
+          this, stream));
+    ActivateStream(stream);
+  }
+}
+
+void
+SpdySession::SetWriteCallbacks(nsAHttpTransaction *aTrans)
+{
+  if (mConnection && (WriteQueueSize() || mOutputQueueUsed))
+      mConnection->ResumeSend(aTrans);
+}
+
+void
+SpdySession::FlushOutputQueue()
+{
+  if (!mSegmentReader || !mOutputQueueUsed)
+    return;
+  
+  nsresult rv;
+  PRUint32 countRead;
+  PRUint32 avail = mOutputQueueUsed - mOutputQueueSent;
+
+  rv = mSegmentReader->
+    OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
+                                     &countRead);
+  LOG3(("SpdySession::FlushOutputQueue %p sz=%d rv=%x actual=%d",
+        this, avail, rv, countRead));
+  
+  // Dont worry about errors on write, we will pick this up as a read error too
+  if (NS_FAILED(rv))
+    return;
+  
+  if (countRead == avail) {
+    mOutputQueueUsed = 0;
+    mOutputQueueSent = 0;
+    return;
+  }
+
+  mOutputQueueSent += countRead;
+  if (mOutputQueueSize - mOutputQueueUsed < kQueueTailRoom) {
+    // The output queue is filling up and we just sent some data out, so
+    // this is a good time to rearrange the output queue.
+
+    mOutputQueueUsed -= mOutputQueueSent;
+    memmove(mOutputQueueBuffer.get(),
+            mOutputQueueBuffer.get() + mOutputQueueSent,
+            mOutputQueueUsed);
+    mOutputQueueSent = 0;
+  }
+}
+
+void
+SpdySession::DontReuse()
+{
+  mShouldGoAway = true;
+  if(!mStreamTransactionHash.Count())
+    Close(NS_OK);
+}
+
+PRUint32
+SpdySession::WriteQueueSize()
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  PRUint32 count = mUrgentForWrite.GetSize() + mReadyForWrite.GetSize();
+
+  if (mPartialFrame)
+    ++count;
+  return count;
+}
+
+void
+SpdySession::ChangeDownstreamState(enum stateType newState)
+{
+  LOG3(("SpdyStream::ChangeDownstreamState() %p from %X to %X",
+        this, mDownstreamState, newState));
+  mDownstreamState = newState;
+
+  if (mDownstreamState == BUFFERING_FRAME_HEADER) {
+    if (mFrameDataLast && mFrameDataStream) {
+      mFrameDataLast = 0;
+      if (!mFrameDataStream->RecvdFin()) {
+        mFrameDataStream->SetRecvdFin(true);
+        --mConcurrent;
+        ProcessPending();
+      }
+    }
+    mFrameBufferUsed = 0;
+    mFrameDataStream = nsnull;
+  }
+  
+  return;
+}
+
+void
+SpdySession::EnsureBuffer(nsAutoArrayPtr<char> &buf,
+                          PRUint32 newSize,
+                          PRUint32 preserve,
+                          PRUint32 &objSize)
+{
+  if (objSize >= newSize)
+    return;
+  
+  objSize = newSize;
+  nsAutoArrayPtr<char> tmp(new char[objSize]);
+  memcpy (tmp, buf, preserve);
+  buf = tmp;
+}
+
+void
+SpdySession::zlibInit()
+{
+  mDownstreamZlib.zalloc = SpdyStream::zlib_allocator;
+  mDownstreamZlib.zfree = SpdyStream::zlib_destructor;
+  mDownstreamZlib.opaque = Z_NULL;
+
+  inflateInit(&mDownstreamZlib);
+
+  mUpstreamZlib.zalloc = SpdyStream::zlib_allocator;
+  mUpstreamZlib.zfree = SpdyStream::zlib_destructor;
+  mUpstreamZlib.opaque = Z_NULL;
+
+  deflateInit(&mUpstreamZlib, Z_DEFAULT_COMPRESSION);
+  deflateSetDictionary(&mUpstreamZlib,
+                       reinterpret_cast<const unsigned char *>
+                       (SpdyStream::kDictionary),
+                       strlen(SpdyStream::kDictionary) + 1);
+
+}
+
+nsresult
+SpdySession::DownstreamUncompress(char *blockStart, PRUint32 blockLen)
+{
+  mDecompressBufferUsed = 0;
+
+  mDownstreamZlib.avail_in = blockLen;
+  mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart);
+
+  do {
+    mDownstreamZlib.next_out =
+      reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
+      mDecompressBufferUsed;
+    mDownstreamZlib.avail_out = mDecompressBufferSize - mDecompressBufferUsed;
+    int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH);
+
+    if (zlib_rv == Z_NEED_DICT)
+      inflateSetDictionary(&mDownstreamZlib,
+                           reinterpret_cast<const unsigned char *>
+                           (SpdyStream::kDictionary),
+                           strlen(SpdyStream::kDictionary) + 1);
+    
+    if (zlib_rv == Z_DATA_ERROR || zlib_rv == Z_MEM_ERROR)
+      return NS_ERROR_FAILURE;
+
+    mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
+      mDownstreamZlib.avail_out;
+    
+    // When there is no more output room, but input still available then
+    // increase the output space
+    if (zlib_rv == Z_OK &&
+        !mDownstreamZlib.avail_out && mDownstreamZlib.avail_in) {
+      LOG3(("SpdySession::DownstreamUncompress %p Large Headers - so far %d",
+            this, mDecompressBufferSize));
+      EnsureBuffer(mDecompressBuffer,
+                   mDecompressBufferSize + 4096,
+                   mDecompressBufferUsed,
+                   mDecompressBufferSize);
+    }
+  }
+  while (mDownstreamZlib.avail_in);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::FindHeader(nsCString name,
+                        nsDependentCSubstring &value)
+{
+  const unsigned char *nvpair = reinterpret_cast<unsigned char *>
+    (mDecompressBuffer.get()) + 2;
+  const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
+    (mDecompressBuffer.get()) + mDecompressBufferUsed;
+  if (lastHeaderByte < nvpair)
+    return NS_ERROR_ILLEGAL_VALUE;
+  PRUint16 numPairs =
+    PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
+  for (PRUint16 index = 0; index < numPairs; ++index) {
+    if (lastHeaderByte < nvpair + 2)
+      return NS_ERROR_ILLEGAL_VALUE;
+    PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
+    if (lastHeaderByte < nvpair + 2 + nameLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+    nsDependentCSubstring nameString =
+      Substring (reinterpret_cast<const char *>(nvpair) + 2,
+                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
+    if (lastHeaderByte < nvpair + 4 + nameLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+    PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
+    if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+    if (nameString.Equals(name)) {
+      value.Assign(((char *)nvpair) + 4 + nameLen, valueLen);
+      return NS_OK;
+    }
+    nvpair += 4 + nameLen + valueLen;
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+SpdySession::ConvertHeaders(nsDependentCSubstring &status,
+                            nsDependentCSubstring &version)
+{
+
+  mFlatHTTPResponseHeaders.Truncate();
+  mFlatHTTPResponseHeadersOut = 0;
+  mFlatHTTPResponseHeaders.SetCapacity(mDecompressBufferUsed + 64);
+
+  // Connection, Keep-Alive and chunked transfer encodings are to be
+  // removed.
+
+  // Content-Length is 'advisory'.. we will not strip it because it can
+  // create UI feedback.
+  
+  mFlatHTTPResponseHeaders.Append(version);
+  mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(" "));
+  mFlatHTTPResponseHeaders.Append(status);
+  mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
+
+  const unsigned char *nvpair = reinterpret_cast<unsigned char *>
+    (mDecompressBuffer.get()) + 2;
+  const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
+    (mDecompressBuffer.get()) + mDecompressBufferUsed;
+
+  if (lastHeaderByte < nvpair)
+    return NS_ERROR_ILLEGAL_VALUE;
+
+  PRUint16 numPairs =
+    PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
+
+  for (PRUint16 index = 0; index < numPairs; ++index) {
+    if (lastHeaderByte < nvpair + 2)
+      return NS_ERROR_ILLEGAL_VALUE;
+
+    PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
+    if (lastHeaderByte < nvpair + 2 + nameLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+
+    nsDependentCSubstring nameString =
+      Substring (reinterpret_cast<const char *>(nvpair) + 2,
+                 reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
+
+    // a null in the name string is particularly wrong because it will
+    // break the fix-up-nulls-in-value-string algorithm.
+    if (nameString.FindChar(0) != -1)
+      return NS_ERROR_ILLEGAL_VALUE;
+
+    if (lastHeaderByte < nvpair + 4 + nameLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+    PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
+    if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
+      return NS_ERROR_ILLEGAL_VALUE;
+    
+    // Look for upper case characters in the name. They are illegal.
+    for (char *cPtr = nameString.BeginWriting();
+         cPtr && cPtr < nameString.EndWriting();
+         ++cPtr) {
+      if (*cPtr <= 'Z' && *cPtr >= 'A') {
+        nsCString toLog(nameString);
+
+        LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
+              "upper case response header found. [%s]\n",
+              this, mFrameDataStream, toLog.get()));
+
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
+    }
+
+    // HTTP Chunked responses are not legal over spdy. We do not need
+    // to look for chunked specifically because it is the only HTTP
+    // allowed default encoding and we did not negotiate further encodings
+    // via TE
+    if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
+      LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
+            "transfer-encoding found. Chunked is invalid and no TE sent.",
+            this, mFrameDataStream));
+
+      return NS_ERROR_ILLEGAL_VALUE;
+    }
+
+    if (!nameString.Equals(NS_LITERAL_CSTRING("version")) &&
+        !nameString.Equals(NS_LITERAL_CSTRING("status")) &&
+        !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
+        !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
+      nsDependentCSubstring valueString =
+        Substring (reinterpret_cast<const char *>(nvpair) + 4 + nameLen,
+                   reinterpret_cast<const char *>(nvpair) + 4 + nameLen +
+                   valueLen);
+      
+      mFlatHTTPResponseHeaders.Append(nameString);
+      mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
+
+      PRInt32 valueIndex;
+      // NULLs are really "\r\nhdr: "
+      while ((valueIndex = valueString.FindChar(0)) != -1) {
+        nsCString replacement = NS_LITERAL_CSTRING("\r\n");
+        replacement.Append(nameString);
+        replacement.Append(NS_LITERAL_CSTRING(": "));
+        valueString.Replace(valueIndex, 1, replacement);
+      }
+
+      mFlatHTTPResponseHeaders.Append(valueString);
+      mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
+    }
+    nvpair += 4 + nameLen + valueLen;
+  }
+
+  mFlatHTTPResponseHeaders.Append(
+    NS_LITERAL_CSTRING("X-Firefox-Spdy: 1\r\n\r\n"));
+  LOG (("decoded response headers are:\n%s",
+        mFlatHTTPResponseHeaders.get()));
+  
+  return NS_OK;
+}
+
+void
+SpdySession::GeneratePing(PRUint32 aID)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::GeneratePing %p 0x%X\n", this, aID));
+
+  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
+               mOutputQueueUsed, mOutputQueueSize);
+  char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  mOutputQueueUsed += 12;
+
+  packet[0] = kFlag_Control;
+  packet[1] = 2;                                  /* version 2 */
+  packet[2] = 0;
+  packet[3] = CONTROL_TYPE_PING;
+  packet[4] = 0;                                  /* flags */
+  packet[5] = 0;
+  packet[6] = 0;
+  packet[7] = 4;                                  /* length */
+  
+  aID = PR_htonl(aID);
+  memcpy (packet + 8, &aID, 4);
+
+  FlushOutputQueue();
+}
+
+void
+SpdySession::GenerateRstStream(PRUint32 aStatusCode, PRUint32 aID)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
+
+  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
+               mOutputQueueUsed, mOutputQueueSize);
+  char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  mOutputQueueUsed += 16;
+
+  packet[0] = kFlag_Control;
+  packet[1] = 2;                                  /* version 2 */
+  packet[2] = 0;
+  packet[3] = CONTROL_TYPE_RST_STREAM;
+  packet[4] = 0;                                  /* flags */
+  packet[5] = 0;
+  packet[6] = 0;
+  packet[7] = 8;                                  /* length */
+  
+  aID = PR_htonl(aID);
+  memcpy (packet + 8, &aID, 4);
+  aStatusCode = PR_htonl(aStatusCode);
+  memcpy (packet + 12, &aStatusCode, 4);
+
+  FlushOutputQueue();
+}
+
+void
+SpdySession::GenerateGoAway()
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::GenerateGoAway %p\n", this));
+
+  EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
+               mOutputQueueUsed, mOutputQueueSize);
+  char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
+  mOutputQueueUsed += 12;
+
+  memset (packet, 0, 12);
+  packet[0] = kFlag_Control;
+  packet[1] = 2;                                  /* version 2 */
+  packet[3] = CONTROL_TYPE_GOAWAY;
+  packet[7] = 4;                                  /* data length */
+  
+  // last-good-stream-id are bytes 8-11, when we accept server push this will
+  // need to be set non zero
+
+  FlushOutputQueue();
+}
+
+void
+SpdySession::CleanupStream(SpdyStream *aStream, nsresult aResult)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::CleanupStream %p %p 0x%x %X\n",
+        this, aStream, aStream->StreamID(), aResult));
+
+  nsresult abortCode = NS_OK;
+
+  if (!aStream->RecvdFin() && aStream->StreamID()) {
+    LOG3(("Stream had not processed recv FIN, sending RST"));
+    GenerateRstStream(RST_CANCEL, aStream->StreamID());
+    --mConcurrent;
+    ProcessPending();
+  }
+  
+  // Check if partial frame writer
+  if (mPartialFrame == aStream) {
+    LOG3(("Stream had active partial write frame - need to abort session"));
+    abortCode = aResult;
+    if (NS_SUCCEEDED(abortCode))
+      abortCode = NS_ERROR_ABORT;
+    
+    mPartialFrame = nsnull;
+  }
+  
+  // Check if partial frame reader
+  if (aStream == mFrameDataStream) {
+    LOG3(("Stream had active partial read frame on close"));
+    ChangeDownstreamState(DISCARD_DATA_FRAME);
+    mFrameDataStream = nsnull;
+  }
+
+  // check the streams blocked on write, this is linear but the list
+  // should be pretty short.
+  PRUint32 size = mReadyForWrite.GetSize();
+  for (PRUint32 count = 0; count < size; ++count) {
+    SpdyStream *stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
+    if (stream != aStream)
+      mReadyForWrite.Push(stream);
+  }
+
+  // Check the streams blocked on urgent (i.e. window update) writing.
+  // This should also be short.
+  size = mUrgentForWrite.GetSize();
+  for (PRUint32 count = 0; count < size; ++count) {
+    SpdyStream *stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
+    if (stream != aStream)
+      mUrgentForWrite.Push(stream);
+  }
+
+  // Remove the stream from the ID hash table. (this one isn't short, which is
+  // why it is hashed.)
+  mStreamIDHash.Remove(aStream->StreamID());
+
+  // Send the stream the close() indication
+  aStream->Close(aResult);
+
+  // removing from the stream transaction hash will
+  // delete the SpdyStream and drop the reference to
+  // its transaction
+  mStreamTransactionHash.Remove(aStream->Transaction());
+
+  if (NS_FAILED(abortCode))
+    Close(abortCode);
+  else if (mShouldGoAway && !mStreamTransactionHash.Count())
+    Close(NS_OK);
+}
+
+nsresult
+SpdySession::HandleSynStream(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM,
+                    "wrong control type");
+  
+  if (self->mFrameDataSize < 12) {
+    LOG3(("SpdySession::HandleSynStream %p SYN_STREAM too short data=%d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  PRUint32 streamID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+
+  LOG3(("SpdySession::HandleSynStream %p recv SYN_STREAM (push) for ID 0x%X.",
+        self, streamID));
+    
+  if (streamID & 0x01) {                   // test for odd stream ID
+    LOG3(("SpdySession::HandleSynStream %p recvd SYN_STREAM id must be even.",
+          self));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  ++(self->mServerPushedResources);
+
+  // Anytime we start using the high bit of stream ID (either client or server)
+  // begin to migrate to a new session.
+  if (streamID >= kMaxStreamID)
+    self->mShouldGoAway = true;
+
+  // todo populate cache. For now, just reject server push p3
+  self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleSynReply(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY,
+                    "wrong control type");
+
+  if (self->mFrameDataSize < 8) {
+    LOG3(("SpdySession::HandleSynReply %p SYN REPLY too short data=%d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  
+  PRUint32 streamID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+  self->mFrameDataStream = self->mStreamIDHash.Get(streamID);
+  if (!self->mFrameDataStream) {
+    LOG3(("SpdySession::HandleSynReply %p lookup streamID in syn_reply "
+          "0x%X failed. NextStreamID = 0x%x", self, streamID,
+          self->mNextStreamID));
+    if (streamID >= self->mNextStreamID)
+      self->GenerateRstStream(RST_INVALID_STREAM, streamID);
+    
+    // It is likely that this is a reply to a stream ID that has been canceled.
+    // For the most part we would like to ignore it, but the header needs to be
+    // be parsed to keep the compression context synchronized
+    self->DownstreamUncompress(self->mFrameBuffer + 14,
+                               self->mFrameDataSize - 6);
+    self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    return NS_OK;
+  }
+  
+  if (!self->mFrameDataStream->SetFullyOpen()) {
+    // "If an endpoint receives multiple SYN_REPLY frames for the same active
+    // stream ID, it must drop the stream, and send a RST_STREAM for the
+    // stream with the error PROTOCOL_ERROR."
+    //
+    // In addition to that we abort the session - this is a serious protocol
+    // violation.
+
+    self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  self->mFrameDataLast = self->mFrameBuffer[4] & kFlag_Data_FIN;
+
+  if (self->mFrameBuffer[4] & kFlag_Data_UNI) {
+    LOG3(("SynReply had unidirectional flag set on it - nonsensical"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  LOG3(("SpdySession::HandleSynReply %p SYN_REPLY for 0x%X fin=%d",
+        self, streamID, self->mFrameDataLast));
+  
+  // The spdystream needs to see flattened http headers
+  // The Frame Buffer currently holds the complete SYN_REPLY
+  // frame. The interesting data is at offset 14, where the
+  // compressed name/value header block lives.
+  // We unpack that into the mDecompressBuffer - we can't do
+  // it streamed because the version and status information
+  // is not guaranteed to be first. This is then finally
+  // converted to HTTP format in mFlatHTTPResponseHeaders
+
+  nsresult rv = self->DownstreamUncompress(self->mFrameBuffer + 14,
+                                           self->mFrameDataSize - 6);
+  if (NS_FAILED(rv))
+    return rv;
+  
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE,
+                        self->mFrameDataSize - 6);
+  PRUint32 ratio =
+    (self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed;
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
+
+  // status and version are required.
+  nsDependentCSubstring status, version;
+  rv = self->FindHeader(NS_LITERAL_CSTRING("status"), status);
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = self->FindHeader(NS_LITERAL_CSTRING("version"), version);
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = self->ConvertHeaders(status, version);
+  if (NS_FAILED(rv))
+    return rv;
+
+  self->ChangeDownstreamState(PROCESSING_CONTROL_SYN_REPLY);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleRstStream(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_RST_STREAM,
+                    "wrong control type");
+
+  if (self->mFrameDataSize != 8) {
+    LOG3(("SpdySession::HandleRstStream %p RST_STREAM wrong length data=%d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  PRUint32 streamID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+
+  self->mDownstreamRstReason =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[3]);
+
+  LOG3(("SpdySession::HandleRstStream %p RST_STREAM Reason Code %u ID %x",
+        self, self->mDownstreamRstReason, streamID));
+
+  if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
+      self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
+    // basically just ignore this
+    self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    return NS_OK;
+  }
+
+  self->mFrameDataStream = self->mStreamIDHash.Get(streamID);
+  if (!self->mFrameDataStream) {
+    LOG3(("SpdySession::HandleRstStream %p lookup streamID for RST Frame "
+          "0x%X failed", self, streamID));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+    
+  self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleSettings(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SETTINGS,
+                    "wrong control type");
+
+  if (self->mFrameDataSize < 4) {
+    LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  PRUint32 numEntries =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+
+  // Ensure frame is large enough for supplied number of entries
+  // Each entry is 8 bytes, frame data is reduced by 4 to account for
+  // the NumEntries value.
+  if ((self->mFrameDataSize - 4) < (numEntries * 8)) {
+    LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  LOG3(("SpdySession::HandleSettings %p SETTINGS Control Frame with %d entries",
+        self, numEntries));
+
+  for (PRUint32 index = 0; index < numEntries; ++index) {
+    // To clarify the v2 spec:
+    // Each entry is a 24 bits of a little endian id
+    // followed by 8 bits of flags
+    // followed by a 32 bit big endian value
+    
+    unsigned char *setting = reinterpret_cast<unsigned char *>
+      (self->mFrameBuffer.get()) + 12 + index * 8;
+
+    PRUint32 id = (setting[2] << 16) + (setting[1] << 8) + setting[0];
+    PRUint32 flags = setting[3];
+    PRUint32 value =  PR_ntohl(reinterpret_cast<PRUint32 *>(setting)[1]);
+
+    LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
+
+    switch (id)
+    {
+    case SETTINGS_TYPE_UPLOAD_BW:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value);
+      break;
+      
+    case SETTINGS_TYPE_DOWNLOAD_BW:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value);
+      break;
+      
+    case SETTINGS_TYPE_RTT:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
+      break;
+      
+    case SETTINGS_TYPE_MAX_CONCURRENT:
+      self->mMaxConcurrent = value;
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
+      break;
+      
+    case SETTINGS_TYPE_CWND:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
+      break;
+      
+    case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
+      break;
+      
+    case SETTINGS_TYPE_INITIAL_WINDOW:
+      Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
+      break;
+      
+    default:
+      break;
+    }
+    
+  }
+  
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleNoop(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_NOOP,
+                    "wrong control type");
+
+  if (self->mFrameDataSize != 0) {
+    LOG3(("SpdySession::HandleNoop %p NOP had data %d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  LOG3(("SpdySession::HandleNoop %p NOP.", self));
+
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandlePing(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_PING,
+                    "wrong control type");
+
+  if (self->mFrameDataSize != 4) {
+    LOG3(("SpdySession::HandlePing %p PING had wrong amount of data %d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  PRUint32 pingID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+
+  LOG3(("SpdySession::HandlePing %p PING ID 0x%X.", self, pingID));
+
+  if (pingID & 0x01) {
+    // We never expect to see an odd PING beacuse we never generate PING.
+      // The spec mandates ignoring this
+    LOG3(("SpdySession::HandlePing %p PING ID from server was odd.",
+          self));
+  }
+  else {
+    self->GeneratePing(pingID);
+  }
+    
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleGoAway(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_GOAWAY,
+                    "wrong control type");
+
+  if (self->mFrameDataSize != 4) {
+    LOG3(("SpdySession::HandleGoAway %p GOAWAY had wrong amount of data %d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  self->mShouldGoAway = true;
+  self->mGoAwayID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+  self->mCleanShutdown = true;
+  
+  LOG3(("SpdySession::HandleGoAway %p GOAWAY Last-Good-ID 0x%X.",
+        self, self->mGoAwayID));
+  self->ResumeRecv(self);
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleHeaders(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_HEADERS,
+                    "wrong control type");
+
+  if (self->mFrameDataSize < 10) {
+    LOG3(("SpdySession::HandleHeaders %p HEADERS had wrong amount of data %d",
+          self, self->mFrameDataSize));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  PRUint32 streamID =
+    PR_ntohl(reinterpret_cast<PRUint32 *>(self->mFrameBuffer.get())[2]);
+
+  // this is actually not legal in the HTTP mapping of SPDY. All
+  // headers are in the syn or syn reply. Log and ignore it.
+
+  LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
+        "They are ignored in the HTTP/SPDY mapping.",
+        self, streamID));
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+nsresult
+SpdySession::HandleWindowUpdate(SpdySession *self)
+{
+  NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE,
+                    "wrong control type");
+  LOG3(("SpdySession::HandleWindowUpdate %p WINDOW UPDATE was "
+        "received. WINDOW UPDATE is no longer defined in v2. Ignoring.",
+        self));
+
+  self->ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+  return NS_OK;
+}
+
+// Used for the hashtable enumeration to propogate OnTransportStatus events
+struct transportStatus
+{
+  nsITransport *transport;
+  nsresult status;
+  PRUint64 progress;
+};
+
+static PLDHashOperator
+StreamTransportStatus(nsAHttpTransaction *key,
+                      nsAutoPtr<SpdyStream> &stream,
+                      void *closure)
+{
+  struct transportStatus *status =
+    static_cast<struct transportStatus *>(closure);
+
+  stream->Transaction()->OnTransportStatus(status->transport,
+                                           status->status,
+                                           status->progress);
+  return PL_DHASH_NEXT;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsAHttpTransaction. It is expected that nsHttpConnection is the caller
+// of these methods
+//-----------------------------------------------------------------------------
+
+void
+SpdySession::OnTransportStatus(nsITransport* aTransport,
+                               nsresult aStatus,
+                               PRUint64 aProgress)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  // nsHttpChannel synthesizes progress events in OnDataAvailable
+  if (aStatus == nsISocketTransport::STATUS_RECEIVING_FROM)
+    return;
+
+  // STATUS_SENDING_TO is handled by SpdyStream
+  if (aStatus == nsISocketTransport::STATUS_SENDING_TO)
+    return;
+
+  struct transportStatus status;
+  
+  status.transport = aTransport;
+  status.status = aStatus;
+  status.progress = aProgress;
+
+  mStreamTransactionHash.Enumerate(StreamTransportStatus, &status);
+}
+
+// ReadSegments() is used to write data to the network. Generally, HTTP
+// request data is pulled from the approriate transaction and
+// converted to SPDY data. Sometimes control data like window-update are
+// generated instead.
+
+nsresult
+SpdySession::ReadSegments(nsAHttpSegmentReader *reader,
+                          PRUint32 count,
+                          PRUint32 *countRead)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  
+  nsresult rv;
+  *countRead = 0;
+
+  // First priority goes to frames that were writing to the network but were
+  // blocked part way through. Then to frames that have no streams (e.g ping
+  // reply) and then third to streams marked urgent (generally they have
+  // window updates), and finally to streams generally
+  // ready to send data frames (http requests).
+
+  LOG3(("SpdySession::ReadSegments %p partial frame stream=%p",
+        this, mPartialFrame));
+
+  SpdyStream *stream = mPartialFrame;
+  mPartialFrame = nsnull;
+
+  if (!stream)
+    stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
+  if (!stream)
+    stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
+  if (!stream) {
+    LOG3(("SpdySession %p could not identify a stream to write; suspending.",
+          this));
+    FlushOutputQueue();
+    SetWriteCallbacks(nsnull);
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  }
+  
+  LOG3(("SpdySession %p will write from SpdyStream %p", this, stream));
+
+  NS_ABORT_IF_FALSE(!mSegmentReader || !reader || (mSegmentReader == reader),
+                    "Inconsistent Write Function Callback");
+
+  if (reader)
+    mSegmentReader = reader;
+  rv = stream->ReadSegments(this, count, countRead);
+
+  FlushOutputQueue();
+
+  if (stream->BlockedOnWrite()) {
+
+    // We are writing a frame out, but it is blocked on the output stream.
+    // Make sure to service that stream next write because we can only
+    // multiplex between complete frames.
+
+    LOG3(("SpdySession::ReadSegments %p dealing with block on write", this));
+
+    NS_ABORT_IF_FALSE(!mPartialFrame, "partial frame should be empty");
+
+    mPartialFrame = stream;
+    SetWriteCallbacks(stream->Transaction());
+    return rv;
+  }
+
+  if (stream->RequestBlockedOnRead()) {
+    
+    // We are blocked waiting for input - either more http headers or
+    // any request body data. When more data from the request stream
+    // becomes available the httptransaction will call conn->ResumeSend().
+    
+    LOG3(("SpdySession::ReadSegments %p dealing with block on read", this));
+
+    // call readsegments again if there are other streams ready
+    // to run in this session
+    if (WriteQueueSize())
+      rv = NS_OK;
+    else
+      rv = NS_BASE_STREAM_WOULD_BLOCK;
+    SetWriteCallbacks(stream->Transaction());
+    return rv;
+  }
+  
+  NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
+                    "Stream Would Block inconsistency");
+  
+  if (NS_FAILED(rv)) {
+    LOG3(("SpdySession::ReadSegments %p returning FAIL code %X",
+          this, rv));
+    return rv;
+  }
+  
+  if (*countRead > 0) {
+    LOG3(("SpdySession::ReadSegments %p stream=%p generated end of frame %d",
+          this, stream, *countRead));
+    mReadyForWrite.Push(stream);
+    SetWriteCallbacks(stream->Transaction());
+    return rv;
+  }
+  
+  LOG3(("SpdySession::ReadSegments %p stream=%p stream send complete",
+        this, stream));
+  
+  // in normal http this is done by nshttpconnection, but that class does not
+  // know which http transaction has made this state transition.
+  stream->Transaction()->
+    OnTransportStatus(mSocketTransport, nsISocketTransport::STATUS_WAITING_FOR,
+                      LL_ZERO);
+  /* we now want to recv data */
+  mConnection->ResumeRecv(stream->Transaction());
+
+  // call readsegments again if there are other streams ready
+  // to go in this session
+  SetWriteCallbacks(stream->Transaction());
+
+  return rv;
+}
+
+// WriteSegments() is used to read data off the socket. Generally this is
+// just the SPDY frame header and from there the appropriate SPDYStream
+// is identified from the Stream-ID. The http transaction associated with
+// that read then pulls in the data directly, which it will feed to
+// OnWriteSegment(). That function will gateway it into http and feed
+// it to the appropriate transaction.
+
+// we call writer->OnWriteSegment to get a spdy header.. and decide if it is
+// data or control.. if it is control, just deal with it.
+// if it is data, identify the spdy stream
+// call stream->WriteSegemnts which can call this::OnWriteSegment to get the
+// data. It always gets full frames if they are part of the stream
+
+nsresult
+SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
+                           PRUint32 count,
+                           PRUint32 *countWritten)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  
+  nsresult rv;
+  *countWritten = 0;
+
+  if (mClosed)
+    return NS_ERROR_FAILURE;
+
+  SetWriteCallbacks(nsnull);
+  
+  // We buffer all control frames and act on them in this layer.
+  // We buffer the first 8 bytes of data frames (the header) but
+  // the actual data is passed through unprocessed.
+  
+  if (mDownstreamState == BUFFERING_FRAME_HEADER) {
+    // The first 8 bytes of every frame is header information that
+    // we are going to want to strip before passing to http. That is
+    // true of both control and data packets.
+    
+    NS_ABORT_IF_FALSE(mFrameBufferUsed < 8,
+                      "Frame Buffer Used Too Large for State");
+
+    rv = writer->OnWriteSegment(mFrameBuffer + mFrameBufferUsed,
+                                8 - mFrameBufferUsed,
+                                countWritten);
+    if (NS_FAILED(rv)) {
+      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+        ResumeRecv(nsnull);
+      }
+      return rv;
+    }
+
+    LogIO(this, nsnull, "Reading Frame Header",
+          mFrameBuffer + mFrameBufferUsed, *countWritten);
+
+    mFrameBufferUsed += *countWritten;
+
+    if (mFrameBufferUsed < 8)
+    {
+      LOG3(("SpdySession::WriteSegments %p "
+            "BUFFERING FRAME HEADER incomplete size=%d",
+            this, mFrameBufferUsed));
+      return rv;
+    }
+
+    // For both control and data frames the second 32 bit word of the header
+    // is 8-flags, 24-length. (network byte order)
+    mFrameDataSize =
+      PR_ntohl(reinterpret_cast<PRUint32 *>(mFrameBuffer.get())[1]);
+    mFrameDataSize &= 0x00ffffff;
+    mFrameDataRead = 0;
+    
+    if (mFrameBuffer[0] & kFlag_Control) {
+      EnsureBuffer(mFrameBuffer, mFrameDataSize + 8, 8, mFrameBufferSize);
+      ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
+      
+      // The first 32 bit word of the header is
+      // 1 ctrl - 15 version - 16 type
+      PRUint16 version =
+        PR_ntohs(reinterpret_cast<PRUint16 *>(mFrameBuffer.get())[0]);
+      version &= 0x7fff;
+      
+      mFrameControlType =
+        PR_ntohs(reinterpret_cast<PRUint16 *>(mFrameBuffer.get())[1]);
+      
+      LOG3(("SpdySession::WriteSegments %p - Control Frame Identified "
+            "type %d version %d data len %d",
+            this, mFrameControlType, version, mFrameDataSize));
+
+      if (mFrameControlType >= CONTROL_TYPE_LAST ||
+          mFrameControlType <= CONTROL_TYPE_FIRST)
+        return NS_ERROR_ILLEGAL_VALUE;
+
+      // The protocol document says this value must be 1 even though this
+      // is known as version 2.. Testing interop indicates that is a typo
+      // in the protocol document
+      if (version != 2) {
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
+    }
+    else {
+      ChangeDownstreamState(PROCESSING_DATA_FRAME);
+
+      PRUint32 streamID =
+        PR_ntohl(reinterpret_cast<PRUint32 *>(mFrameBuffer.get())[0]);
+      mFrameDataStream = mStreamIDHash.Get(streamID);
+      if (!mFrameDataStream) {
+        LOG3(("SpdySession::WriteSegments %p lookup streamID 0x%X failed. "
+              "Next = 0x%x", this, streamID, mNextStreamID));
+        if (streamID >= mNextStreamID)
+          GenerateRstStream(RST_INVALID_STREAM, streamID);
+          ChangeDownstreamState(DISCARD_DATA_FRAME);
+      }
+      mFrameDataLast = (mFrameBuffer[4] & kFlag_Data_FIN);
+      Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD, mFrameDataSize >> 10);
+      LOG3(("Start Processing Data Frame. "
+            "Session=%p Stream ID 0x%x Stream Ptr %p Fin=%d Len=%d",
+            this, streamID, mFrameDataStream, mFrameDataLast, mFrameDataSize));
+
+      if (mFrameBuffer[4] & kFlag_Data_ZLIB) {
+        LOG3(("Data flag has ZLIB flag set which is not valid >=2 spdy"));
+        return NS_ERROR_ILLEGAL_VALUE;
+      }
+    }
+  }
+
+  if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
+    if (mDownstreamRstReason == RST_REFUSED_STREAM)
+      rv = NS_ERROR_NET_RESET;            //we can retry this 100% safely
+    else if (mDownstreamRstReason == RST_CANCEL ||
+             mDownstreamRstReason == RST_PROTOCOL_ERROR ||
+             mDownstreamRstReason == RST_INTERNAL_ERROR ||
+             mDownstreamRstReason == RST_UNSUPPORTED_VERSION)
+      rv = NS_ERROR_NET_INTERRUPT;
+    else
+      rv = NS_ERROR_ILLEGAL_VALUE;
+
+    if (mDownstreamRstReason != RST_REFUSED_STREAM &&
+        mDownstreamRstReason != RST_CANCEL)
+      mShouldGoAway = true;
+
+    // mFrameDataStream is reset by ChangeDownstreamState
+    SpdyStream *stream = mFrameDataStream;
+    ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    CleanupStream(stream, rv);
+    return NS_OK;
+  }
+
+  if (mDownstreamState == PROCESSING_DATA_FRAME ||
+      mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
+
+    mSegmentWriter = writer;
+    rv = mFrameDataStream->WriteSegments(this, count, countWritten);
+    mSegmentWriter = nsnull;
+
+    if (rv == NS_BASE_STREAM_CLOSED) {
+      // This will happen when the transaction figures out it is EOF, generally
+      // due to a content-length match being made
+      SpdyStream *stream = mFrameDataStream;
+      if (mFrameDataRead == mFrameDataSize)
+        ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      CleanupStream(stream, NS_OK);
+      NS_ABORT_IF_FALSE(!mNeedsCleanup, "double cleanup out of data frame");
+      return NS_OK;
+    }
+    
+    if (mNeedsCleanup) {
+      CleanupStream(mNeedsCleanup, NS_OK);
+      mNeedsCleanup = nsnull;
+    }
+
+    // In v3 this is where we would generate a window update
+
+    return rv;
+  }
+
+  if (mDownstreamState == DISCARD_DATA_FRAME) {
+    char trash[4096];
+    PRUint32 count = NS_MIN(4096U, mFrameDataSize - mFrameDataRead);
+
+    if (!count) {
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      *countWritten = 1;
+      return NS_OK;
+    }
+
+    rv = writer->OnWriteSegment(trash, count, countWritten);
+
+    if (NS_FAILED(rv)) {
+      // maybe just blocked reading from network
+      ResumeRecv(nsnull);
+      return rv;
+    }
+
+    LogIO(this, nsnull, "Discarding Frame", trash, *countWritten);
+
+    mFrameDataRead += *countWritten;
+
+    if (mFrameDataRead == mFrameDataSize)
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    return rv;
+  }
+  
+  NS_ABORT_IF_FALSE(mDownstreamState == BUFFERING_CONTROL_FRAME,
+                    "Not in Bufering Control Frame State");
+  NS_ABORT_IF_FALSE(mFrameBufferUsed == 8,
+                    "Frame Buffer Header Not Present");
+
+  rv = writer->OnWriteSegment(mFrameBuffer + 8 + mFrameDataRead,
+                              mFrameDataSize - mFrameDataRead,
+                              countWritten);
+  if (NS_FAILED(rv)) {
+    // maybe just blocked reading from network
+    ResumeRecv(nsnull);
+    return rv;
+  }
+
+  LogIO(this, nsnull, "Reading Control Frame",
+        mFrameBuffer + 8 + mFrameDataRead, *countWritten);
+
+  mFrameDataRead += *countWritten;
+
+  if (mFrameDataRead != mFrameDataSize)
+    return NS_OK;
+
+  rv = sControlFunctions[mFrameControlType](this);
+
+  NS_ABORT_IF_FALSE(NS_FAILED(rv) ||
+                    mDownstreamState != BUFFERING_CONTROL_FRAME,
+                    "Control Handler returned OK but did not change state");
+
+  if (mShouldGoAway && !mStreamTransactionHash.Count())
+    Close(NS_OK);
+  return rv;
+}
+
+void
+SpdySession::Close(nsresult aReason)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  if (mClosed)
+    return;
+
+  LOG3(("SpdySession::Close %p %X", this, aReason));
+
+  mClosed = true;
+  mStreamTransactionHash.Enumerate(Shutdown, this);
+  GenerateGoAway();
+  mConnection = nsnull;
+}
+
+void
+SpdySession::CloseTransaction(nsAHttpTransaction *aTransaction,
+                              nsresult aResult)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::CloseTransaction %p %p %x", this, aTransaction, aResult));
+
+  // Generally this arrives as a cancel event from the connection manager.
+
+  // need to find the stream and call CleanupStream() on it.
+  SpdyStream *stream = mStreamTransactionHash.Get(aTransaction);
+  if (!stream) {
+    LOG3(("SpdySession::CloseTransaction %p %p %x - not found.",
+          this, aTransaction, aResult));
+    return;
+  }
+  LOG3(("SpdySession::CloseTranscation probably a cancel. "
+        "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
+        this, aTransaction, aResult, stream->StreamID(), stream));
+  CleanupStream(stream, aResult);
+  ResumeRecv(this);
+}
+
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentReader
+//-----------------------------------------------------------------------------
+
+nsresult
+SpdySession::OnReadSegment(const char *buf,
+                           PRUint32 count,
+                           PRUint32 *countRead)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  
+  nsresult rv;
+  
+  if (!mOutputQueueUsed && mSegmentReader) {
+
+    // try and write directly without output queue
+    rv = mSegmentReader->OnReadSegment(buf, count, countRead);
+    if (NS_SUCCEEDED(rv) || (rv != NS_BASE_STREAM_WOULD_BLOCK))
+      return rv;
+  }
+  
+  if (mOutputQueueUsed + count > mOutputQueueSize)
+    FlushOutputQueue();
+
+  if (mOutputQueueUsed + count > mOutputQueueSize)
+    count = mOutputQueueSize - mOutputQueueUsed;
+
+  if (!count)
+    return NS_BASE_STREAM_WOULD_BLOCK;
+  
+  memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
+  mOutputQueueUsed += count;
+  *countRead = count;
+
+  FlushOutputQueue();
+    
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentWriter
+//-----------------------------------------------------------------------------
+
+nsresult
+SpdySession::OnWriteSegment(char *buf,
+                            PRUint32 count,
+                            PRUint32 *countWritten)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
+  nsresult rv;
+
+  if (mDownstreamState == PROCESSING_DATA_FRAME) {
+
+    if (mFrameDataLast &&
+        mFrameDataRead == mFrameDataSize) {
+      // This will result in Close() being called
+      mNeedsCleanup = mFrameDataStream;
+
+      LOG3(("SpdySession::OnWriteSegment %p - recorded downstream fin of "
+            "stream %p 0x%X", this, mFrameDataStream,
+            mFrameDataStream->StreamID()));
+      *countWritten = 0;
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      return NS_BASE_STREAM_CLOSED;
+    }
+    
+    count = NS_MIN(count, mFrameDataSize - mFrameDataRead);
+    rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
+    if (NS_FAILED(rv))
+      return rv;
+
+    LogIO(this, mFrameDataStream, "Reading Data Frame", buf, *countWritten);
+
+    mFrameDataRead += *countWritten;
+    
+    if ((mFrameDataRead == mFrameDataSize) && !mFrameDataLast)
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+
+    return rv;
+  }
+  
+  if (mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
+    
+    if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
+        mFrameDataLast) {
+      *countWritten = 0;
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+      return NS_BASE_STREAM_CLOSED;
+    }
+      
+    count = NS_MIN(count,
+                   mFlatHTTPResponseHeaders.Length() -
+                   mFlatHTTPResponseHeadersOut);
+    memcpy(buf,
+           mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
+           count);
+    mFlatHTTPResponseHeadersOut += count;
+    *countWritten = count;
+
+    if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
+        !mFrameDataLast)
+      ChangeDownstreamState(BUFFERING_FRAME_HEADER);
+    return NS_OK;
+  }
+
+  return NS_ERROR_UNEXPECTED;
+}
+
+//-----------------------------------------------------------------------------
+// Modified methods of nsAHttpConnection
+//-----------------------------------------------------------------------------
+
+nsresult
+SpdySession::ResumeSend(nsAHttpTransaction *caller)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  LOG3(("SpdySession::ResumeSend %p caller=%p", this, caller));
+
+  // a trapped signal from the http transaction to the connection that
+  // it is no longer blocked on read.
+
+  if (!mConnection)
+    return NS_ERROR_FAILURE;
+
+  SpdyStream *stream = mStreamTransactionHash.Get(caller);
+  if (stream)
+    mReadyForWrite.Push(stream);
+  else
+    LOG3(("SpdySession::ResumeSend %p caller %p not found", this, caller));
+  
+  return mConnection->ResumeSend(caller);
+}
+
+nsresult
+SpdySession::ResumeRecv(nsAHttpTransaction *caller)
+{
+  if (!mConnection)
+    return NS_ERROR_FAILURE;
+
+  return mConnection->ResumeRecv(caller);
+}
+
+bool
+SpdySession::IsPersistent()
+{
+  return PR_TRUE;
+}
+
+nsresult
+SpdySession::TakeTransport(nsISocketTransport **,
+                           nsIAsyncInputStream **,
+                           nsIAsyncOutputStream **)
+{
+  NS_ABORT_IF_FALSE(false, "TakeTransport of SpdySession");
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsHttpConnection *
+SpdySession::TakeHttpConnection()
+{
+  NS_ABORT_IF_FALSE(false, "TakeHttpConnection of SpdySession");
+  return nsnull;
+}
+
+nsISocketTransport *
+SpdySession::Transport()
+{
+    if (!mConnection)
+        return nsnull;
+    return mConnection->Transport();
+}
+
+//-----------------------------------------------------------------------------
+// unused methods of nsAHttpTransaction
+// We can be sure of this because SpdySession is only constructed in
+// nsHttpConnection and is never passed out of that object
+//-----------------------------------------------------------------------------
+
+void
+SpdySession::SetConnection(nsAHttpConnection *)
+{
+  // This is unexpected
+  NS_ABORT_IF_FALSE(false, "SpdySession::SetConnection()");
+}
+
+void
+SpdySession::GetSecurityCallbacks(nsIInterfaceRequestor **,
+                                  nsIEventTarget **)
+{
+  // This is unexpected
+  NS_ABORT_IF_FALSE(false, "SpdySession::GetSecurityCallbacks()");
+}
+
+void
+SpdySession::SetSSLConnectFailed()
+{
+  NS_ABORT_IF_FALSE(false, "SpdySession::SetSSLConnectFailed()");
+}
+
+bool
+SpdySession::IsDone()
+{
+  NS_ABORT_IF_FALSE(false, "SpdySession::IsDone()");
+  return PR_FALSE;
+}
+
+nsresult
+SpdySession::Status()
+{
+  NS_ABORT_IF_FALSE(false, "SpdySession::Status()");
+  return NS_ERROR_UNEXPECTED;
+}
+
+PRUint32
+SpdySession::Available()
+{
+  NS_ABORT_IF_FALSE(false, "SpdySession::Available()");
+  return 0;
+}
+
+nsHttpRequestHead *
+SpdySession::RequestHead()
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(false,
+                    "SpdySession::RequestHead() "
+                    "should not be called after SPDY is setup");
+  return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Pass through methods of nsAHttpConnection
+//-----------------------------------------------------------------------------
+
+nsAHttpConnection *
+SpdySession::Connection()
+{
+    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    return mConnection;
+}
+
+nsresult
+SpdySession::OnHeadersAvailable(nsAHttpTransaction *transaction,
+                                nsHttpRequestHead *requestHead,
+                                nsHttpResponseHead *responseHead,
+                                bool *reset)
+{
+  return mConnection->OnHeadersAvailable(transaction,
+                                         requestHead,
+                                         responseHead,
+                                         reset);
+}
+
+void
+SpdySession::GetConnectionInfo(nsHttpConnectionInfo **connInfo)
+{
+  mConnection->GetConnectionInfo(connInfo);
+}
+
+void
+SpdySession::GetSecurityInfo(nsISupports **supports)
+{
+  mConnection->GetSecurityInfo(supports);
+}
+
+bool
+SpdySession::IsReused()
+{
+  return mConnection->IsReused();
+}
+
+nsresult
+SpdySession::PushBack(const char *buf, PRUint32 len)
+{
+  return mConnection->PushBack(buf, len);
+}
+
+bool
+SpdySession::LastTransactionExpectedNoContent()
+{
+  return mConnection->LastTransactionExpectedNoContent();
+}
+
+void
+SpdySession::SetLastTransactionExpectedNoContent(bool val)
+{
+  mConnection->SetLastTransactionExpectedNoContent(val);
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/SpdySession.h
@@ -0,0 +1,322 @@
+/* -*- 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 mozilla_net_SpdySession_h
+#define mozilla_net_SpdySession_h
+
+// SPDY as defined by
+// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
+
+#include "nsAHttpTransaction.h"
+#include "nsAHttpConnection.h"
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsDeque.h"
+#include "nsHashKeys.h"
+#include "zlib.h"
+
+class nsHttpConnection;
+class nsISocketTransport;
+
+namespace mozilla { namespace net {
+
+class SpdyStream;
+
+class SpdySession : public nsAHttpTransaction
+                  , public nsAHttpConnection
+                  , public nsAHttpSegmentReader
+                  , public nsAHttpSegmentWriter
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSAHTTPTRANSACTION
+  NS_DECL_NSAHTTPCONNECTION
+  NS_DECL_NSAHTTPSEGMENTREADER
+  NS_DECL_NSAHTTPSEGMENTWRITER
+
+  SpdySession(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
+  ~SpdySession();
+
+  bool AddStream(nsAHttpTransaction *, PRInt32);
+  bool CanReuse() { return !mShouldGoAway && !mClosed; }
+  void DontReuse();
+  bool RoomForMoreStreams();
+  PRUint32 RegisterStreamID(SpdyStream *);
+
+  const static PRUint8 kFlag_Control   = 0x80;
+
+  const static PRUint8 kFlag_Data_FIN  = 0x01;
+  const static PRUint8 kFlag_Data_UNI  = 0x02;
+  const static PRUint8 kFlag_Data_ZLIB = 0x02;
+  
+  const static PRUint8 kPri00   = 0x00;
+  const static PRUint8 kPri01   = 0x40;
+  const static PRUint8 kPri02   = 0x80;
+  const static PRUint8 kPri03   = 0xC0;
+
+  enum
+  {
+    CONTROL_TYPE_FIRST = 0,
+    CONTROL_TYPE_SYN_STREAM = 1,
+    CONTROL_TYPE_SYN_REPLY = 2,
+    CONTROL_TYPE_RST_STREAM = 3,
+    CONTROL_TYPE_SETTINGS = 4,
+    CONTROL_TYPE_NOOP = 5,
+    CONTROL_TYPE_PING = 6,
+    CONTROL_TYPE_GOAWAY = 7,
+    CONTROL_TYPE_HEADERS = 8,
+    CONTROL_TYPE_WINDOW_UPDATE = 9,               /* no longer in v2 */
+    CONTROL_TYPE_LAST = 10
+  };
+
+  enum
+  {
+    RST_PROTOCOL_ERROR = 1,
+    RST_INVALID_STREAM = 2,
+    RST_REFUSED_STREAM = 3,
+    RST_UNSUPPORTED_VERSION = 4,
+    RST_CANCEL = 5,
+    RST_INTERNAL_ERROR = 6,
+    RST_FLOW_CONTROL_ERROR = 7,
+    RST_BAD_ASSOC_STREAM = 8
+  };
+
+  enum
+  {
+    SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
+    SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
+    SETTINGS_TYPE_RTT = 3, // ms
+    SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
+    SETTINGS_TYPE_CWND = 5, // packets
+    SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
+    SETTINGS_TYPE_INITIAL_WINDOW = 7  // bytes. Not used in v2.
+  };
+
+  // This should be big enough to hold all of your control packets,
+  // but if it needs to grow for huge headers it can do so dynamically.
+  // About 1% of requests to SPDY google services seem to be > 1000
+  // with all less than 2000.
+  const static PRUint32 kDefaultBufferSize = 2000;
+
+  const static PRUint32 kDefaultQueueSize =  16000;
+  const static PRUint32 kQueueTailRoom    =  4000;
+  const static PRUint32 kSendingChunkSize = 4000;
+  const static PRUint32 kDefaultMaxConcurrent = 100;
+  const static PRUint32 kMaxStreamID = 0x7800000;
+  
+  static nsresult HandleSynStream(SpdySession *);
+  static nsresult HandleSynReply(SpdySession *);
+  static nsresult HandleRstStream(SpdySession *);
+  static nsresult HandleSettings(SpdySession *);
+  static nsresult HandleNoop(SpdySession *);
+  static nsresult HandlePing(SpdySession *);
+  static nsresult HandleGoAway(SpdySession *);
+  static nsresult HandleHeaders(SpdySession *);
+  static nsresult HandleWindowUpdate(SpdySession *);
+
+  static void EnsureBuffer(nsAutoArrayPtr<char> &,
+                           PRUint32, PRUint32, PRUint32 &);
+
+  // For writing the SPDY data stream to LOG4
+  static void LogIO(SpdySession *, SpdyStream *, const char *,
+                    const char *, PRUint32);
+
+private:
+
+  enum stateType {
+    BUFFERING_FRAME_HEADER,
+    BUFFERING_CONTROL_FRAME,
+    PROCESSING_DATA_FRAME,
+    DISCARD_DATA_FRAME,
+    PROCESSING_CONTROL_SYN_REPLY,
+    PROCESSING_CONTROL_RST_STREAM
+  };
+
+  PRUint32    WriteQueueSize();
+  void        ChangeDownstreamState(enum stateType);
+  nsresult    DownstreamUncompress(char *, PRUint32);
+  void        zlibInit();
+  nsresult    FindHeader(nsCString, nsDependentCSubstring &);
+  nsresult    ConvertHeaders(nsDependentCSubstring &,
+                             nsDependentCSubstring &);
+  void        GeneratePing(PRUint32);
+  void        GenerateRstStream(PRUint32, PRUint32);
+  void        GenerateGoAway();
+  void        CleanupStream(SpdyStream *, nsresult);
+
+  void        SetWriteCallbacks(nsAHttpTransaction *);
+  void        FlushOutputQueue();
+
+  bool        RoomForMoreConcurrent();
+  void        ActivateStream(SpdyStream *);
+  void        ProcessPending();
+
+  static PLDHashOperator Shutdown(nsAHttpTransaction *,
+                                  nsAutoPtr<SpdyStream> &,
+                                  void *);
+
+  // This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken
+  // from the first transcation on this session. That object contains the
+  // pointer to the real network-level nsHttpConnection object.
+  nsRefPtr<nsAHttpConnection> mConnection;
+
+  // The underlying socket transport object is needed to propogate some events
+  nsISocketTransport         *mSocketTransport;
+
+  // These are temporary state variables to hold the argument to
+  // Read/WriteSegments so it can be accessed by On(read/write)segment
+  // further up the stack.
+  nsAHttpSegmentReader       *mSegmentReader;
+  nsAHttpSegmentWriter       *mSegmentWriter;
+
+  PRUint32          mSendingChunkSize;        /* the transmisison chunk size */
+  PRUint32          mNextStreamID;            /* 24 bits */
+  PRUint32          mConcurrentHighWater;     /* max parallelism on session */
+
+  stateType         mDownstreamState; /* in frame, between frames, etc..  */
+
+  // Maintain 5 indexes - one by stream ID, one by transaction ptr,
+  // one list of streams ready to write, one list of streams that are queued
+  // due to max parallelism settings, and one list of streams
+  // that must be given priority to write for window updates. The objects
+  // are not ref counted - they get destryoed
+  // by the nsClassHashtable implementation when they are removed from
+  // there.
+  nsDataHashtable<nsUint32HashKey, SpdyStream *>      mStreamIDHash;
+  nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
+                   SpdyStream>                        mStreamTransactionHash;
+  nsDeque                                             mReadyForWrite;
+  nsDeque                                             mQueuedStreams;
+
+  // UrgentForWrite is meant to carry window updates. They were defined in
+  // the v2 spec but apparently never implemented so are now scheduled to
+  // be removed. But they will be reintroduced for v3, so we will leave
+  // this queue in place to ease that transition.
+  nsDeque           mUrgentForWrite;
+
+  // If we block while wrting out a frame then this points to the stream
+  // that was blocked. When writing again that stream must be the first
+  // one to write. It is null if there is not a partial frame.
+  SpdyStream        *mPartialFrame;
+
+  // Compression contexts for header transport using deflate.
+  // SPDY compresses only HTTP headers and does not reset zlib in between
+  // frames.
+  z_stream            mDownstreamZlib;
+  z_stream            mUpstreamZlib;
+
+  // mFrameBuffer is used to store received control packets and the 8 bytes
+  // of header on data packets
+  PRUint32             mFrameBufferSize;
+  PRUint32             mFrameBufferUsed;
+  nsAutoArrayPtr<char> mFrameBuffer;
+  
+  // mFrameDataSize/Read are used for tracking the amount of data consumed
+  // in a data frame. the data itself is not buffered in spdy
+  // The frame size is mFrameDataSize + the constant 8 byte header
+  PRUint32             mFrameDataSize;
+  PRUint32             mFrameDataRead;
+  bool                 mFrameDataLast; // This frame was marked FIN
+
+  // When a frame has been received that is addressed to a particular stream
+  // (e.g. a data frame after the stream-id has been decoded), this points
+  // to the stream.
+  SpdyStream          *mFrameDataStream;
+  
+  // A state variable to cleanup a closed stream after the stack has unwound.
+  SpdyStream          *mNeedsCleanup;
+
+  // The CONTROL_TYPE value for a control frame
+  PRUint32             mFrameControlType;
+
+  // This reason code in the last processed RESET frame
+  PRUint32             mDownstreamRstReason;
+
+  // These are used for decompressing downstream spdy response headers
+  // This is done at the session level because sometimes the stream
+  // has already been canceled but the decompression still must happen
+  // to keep the zlib state correct for the next state of headers.
+  PRUint32             mDecompressBufferSize;
+  PRUint32             mDecompressBufferUsed;
+  nsAutoArrayPtr<char> mDecompressBuffer;
+
+  // for the conversion of downstream http headers into spdy formatted headers
+  nsCString            mFlatHTTPResponseHeaders;
+  PRUint32             mFlatHTTPResponseHeadersOut;
+
+  // when set, the session will go away when it reaches 0 streams
+  bool                 mShouldGoAway;
+
+  // the session has received a nsAHttpTransaction::Close()  call
+  bool                 mClosed;
+
+  // the session received a GoAway frame with a valid GoAwayID
+  bool                 mCleanShutdown;
+
+  // If a GoAway message was received this is the ID of the last valid
+  // stream. 0 otherwise. (0 is never a valid stream id.)
+  PRUint32             mGoAwayID;
+
+  // The limit on number of concurrent streams for this session. Normally it
+  // is basically unlimited, but the SETTINGS control message from the
+  // server might bring it down.
+  PRUint32             mMaxConcurrent;
+
+  // The actual number of concurrent streams at this moment. Generally below
+  // mMaxConcurrent, but the max can be lowered in real time to a value
+  // below the current value
+  PRUint32             mConcurrent;
+
+  // The number of server initiated SYN-STREAMS, tracked for telemetry
+  PRUint32             mServerPushedResources;
+
+  // This is a output queue of bytes ready to be written to the SSL stream.
+  // When that streams returns WOULD_BLOCK on direct write the bytes get
+  // coalesced together here. This results in larger writes to the SSL layer.
+  // The buffer is not dynamically grown to accomodate stream writes, but
+  // does expand to accept infallible session wide frames like GoAway and RST.
+  PRUint32             mOutputQueueSize;
+  PRUint32             mOutputQueueUsed;
+  PRUint32             mOutputQueueSent;
+  nsAutoArrayPtr<char> mOutputQueueBuffer;
+};
+
+}} // namespace mozilla::net
+
+#endif // mozilla_net_SpdySession_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/SpdyStream.cpp
@@ -0,0 +1,849 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "nsHttp.h"
+#include "SpdySession.h"
+#include "SpdyStream.h"
+#include "nsAlgorithm.h"
+#include "prnetdb.h"
+#include "nsHttpRequestHead.h"
+#include "mozilla/Telemetry.h"
+#include "nsISocketTransport.h"
+#include "nsISupportsPriority.h"
+
+#ifdef DEBUG
+// defined by the socket transport service while active
+extern PRThread *gSocketThread;
+#endif
+
+namespace mozilla {
+namespace net {
+
+SpdyStream::SpdyStream(nsAHttpTransaction *httpTransaction,
+                       SpdySession *spdySession,
+                       nsISocketTransport *socketTransport,
+                       PRUint32 chunkSize,
+                       z_stream *compressionContext,
+                       PRInt32 priority)
+  : mUpstreamState(GENERATING_SYN_STREAM),
+    mTransaction(httpTransaction),
+    mSession(spdySession),
+    mSocketTransport(socketTransport),
+    mSegmentReader(nsnull),
+    mSegmentWriter(nsnull),
+    mStreamID(0),
+    mChunkSize(chunkSize),
+    mSynFrameComplete(0),
+    mBlockedOnWrite(0),
+    mRequestBlockedOnRead(0),
+    mSentFinOnData(0),
+    mRecvdFin(0),
+    mFullyOpen(0),
+    mTxInlineFrameAllocation(SpdySession::kDefaultBufferSize),
+    mTxInlineFrameSize(0),
+    mTxInlineFrameSent(0),
+    mTxStreamFrameSize(0),
+    mTxStreamFrameSent(0),
+    mZlib(compressionContext),
+    mRequestBodyLen(0),
+    mPriority(priority)
+{
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+  LOG3(("SpdyStream::SpdyStream %p", this));
+
+  mTxInlineFrame = new char[mTxInlineFrameAllocation];
+}
+
+SpdyStream::~SpdyStream()
+{
+}
+
+// ReadSegments() is used to write data down the socket. Generally, HTTP
+// request data is pulled from the approriate transaction and
+// converted to SPDY data. Sometimes control data like a window-update is
+// generated instead.
+
+nsresult
+SpdyStream::ReadSegments(nsAHttpSegmentReader *reader,
+                         PRUint32 count,
+                         PRUint32 *countRead)
+{
+  LOG3(("SpdyStream %p ReadSegments reader=%p count=%d state=%x",
+        this, reader, count, mUpstreamState));
+
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  
+  nsresult rv = NS_ERROR_UNEXPECTED;
+  mBlockedOnWrite = 0;
+  mRequestBlockedOnRead = 0;
+
+  switch (mUpstreamState) {
+  case GENERATING_SYN_STREAM:
+  case GENERATING_REQUEST_BODY:
+  case SENDING_REQUEST_BODY:
+    // Call into the HTTP Transaction to generate the HTTP request
+    // stream. That stream will show up in OnReadSegment().
+    mSegmentReader = reader;
+    rv = mTransaction->ReadSegments(this, count, countRead);
+    mSegmentReader = nsnull;
+
+    if (NS_SUCCEEDED(rv) &&
+        mUpstreamState == GENERATING_SYN_STREAM &&
+        !mSynFrameComplete)
+      mBlockedOnWrite = 1;
+    
+    // Mark that we are blocked on read if we the http transaction
+    // is going to get us going again.
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mBlockedOnWrite)
+      mRequestBlockedOnRead = 1;
+
+    if (!mBlockedOnWrite && NS_SUCCEEDED(rv) && (!*countRead)) {
+      LOG3(("ReadSegments %p Send Request data complete from %x",
+            this, mUpstreamState));
+      if (mSentFinOnData) {
+        ChangeState(UPSTREAM_COMPLETE);
+      }
+      else {
+        GenerateDataFrameHeader(0, true);
+        ChangeState(SENDING_FIN_STREAM);
+        mBlockedOnWrite = 1;
+        rv = NS_BASE_STREAM_WOULD_BLOCK;
+      }
+    }
+
+    break;
+
+  case SENDING_SYN_STREAM:
+    // We were trying to send the SYN-STREAM but only got part of it out
+    // before being blocked. Try and send more.
+    mSegmentReader = reader;
+    rv = TransmitFrame(nsnull, nsnull);
+    mSegmentReader = nsnull;
+    *countRead = 0;
+    if (NS_SUCCEEDED(rv))
+      rv = NS_BASE_STREAM_WOULD_BLOCK;
+
+    if (!mTxInlineFrameSize) {
+      if (mSentFinOnData) {
+        ChangeState(UPSTREAM_COMPLETE);
+        rv = NS_OK;
+      }
+      else {
+        ChangeState(GENERATING_REQUEST_BODY);
+        mBlockedOnWrite = 1;
+      }
+    }
+    break;
+
+  case SENDING_FIN_STREAM:
+    // We were trying to send the SYN-STREAM but only got part of it out
+    // before being blocked. Try and send more.
+    if (!mSentFinOnData) {
+      mSegmentReader = reader;
+      rv = TransmitFrame(nsnull, nsnull);
+      mSegmentReader = nsnull;
+      if (!mTxInlineFrameSize)
+        ChangeState(UPSTREAM_COMPLETE);
+    }
+    else {
+      rv = NS_OK;
+      mTxInlineFrameSize = 0;         // cancel fin data packet
+      ChangeState(UPSTREAM_COMPLETE);
+    }
+    
+    *countRead = 0;
+
+    // don't change OK to WOULD BLOCK. we are really done sending if OK
+    break;
+
+  default:
+    NS_ABORT_IF_FALSE(false, "SpdyStream::ReadSegments unknown state");
+    break;
+  }
+
+  return rv;
+}
+
+// WriteSegments() is used to read data off the socket. Generally this is
+// just the SPDY frame header and from there the appropriate SPDYStream
+// is identified from the Stream-ID. The http transaction associated with
+// that read then pulls in the data directly.
+
+nsresult
+SpdyStream::WriteSegments(nsAHttpSegmentWriter *writer,
+                          PRUint32 count,
+                          PRUint32 *countWritten)
+{
+  LOG3(("SpdyStream::WriteSegments %p count=%d state=%x",
+        this, count, mUpstreamState));
+  
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(!mSegmentWriter, "segment writer in progress");
+
+  mSegmentWriter = writer;
+  nsresult rv = mTransaction->WriteSegments(writer, count, countWritten);
+  mSegmentWriter = nsnull;
+  return rv;
+}
+
+PLDHashOperator
+SpdyStream::hdrHashEnumerate(const nsACString &key,
+                             nsAutoPtr<nsCString> &value,
+                             void *closure)
+{
+  SpdyStream *self = static_cast<SpdyStream *>(closure);
+
+  self->CompressToFrame(key);
+  self->CompressToFrame(value.get());
+  return PL_DHASH_NEXT;
+}
+
+nsresult
+SpdyStream::ParseHttpRequestHeaders(const char *buf,
+                                    PRUint32 avail,
+                                    PRUint32 *countUsed)
+{
+  // Returns NS_OK even if the headers are incomplete
+  // set mSynFrameComplete flag if they are complete
+
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(mUpstreamState == GENERATING_SYN_STREAM, "wrong state");
+
+  LOG3(("SpdyStream::ParseHttpRequestHeaders %p avail=%d state=%x",
+        this, avail, mUpstreamState));
+
+  mFlatHttpRequestHeaders.Append(buf, avail);
+
+  // We can use the simple double crlf because firefox is the
+  // only client we are parsing
+  PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
+  
+  if (endHeader == -1) {
+    // We don't have all the headers yet
+    LOG3(("SpdyStream::ParseHttpRequestHeaders %p "
+          "Need more header bytes. Len = %d",
+          this, mFlatHttpRequestHeaders.Length()));
+    *countUsed = avail;
+    return NS_OK;
+  }
+           
+  // We have recvd all the headers, trim the local
+  // buffer of the final empty line, and set countUsed to reflect
+  // the whole header has been consumed.
+  PRUint32 oldLen = mFlatHttpRequestHeaders.Length();
+  mFlatHttpRequestHeaders.SetLength(endHeader + 2);
+  *countUsed = avail - (oldLen - endHeader) + 4;
+  mSynFrameComplete = 1;
+
+  // It is now OK to assign a streamID that we are assured will
+  // be monotonically increasing amongst syn-streams on this
+  // session
+  mStreamID = mSession->RegisterStreamID(this);
+  NS_ABORT_IF_FALSE(mStreamID & 1,
+                    "Spdy Stream Channel ID must be odd");
+
+  if (mStreamID >= 0x80000000) {
+    // streamID must fit in 31 bits. This is theoretically possible
+    // because stream ID assignment is asynchronous to stream creation
+    // because of the protocol requirement that the ID in syn-stream
+    // be monotonically increasing. In reality this is really not possible
+    // because new streams stop being added to a session with 0x10000000 / 2
+    // IDs still available and no race condition is going to bridge that gap,
+    // so we can be comfortable on just erroring out for correctness in that
+    // case.
+    LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // Now we need to convert the flat http headers into a set
+  // of SPDY headers..  writing to mTxInlineFrame{sz}
+
+  mTxInlineFrame[0] = SpdySession::kFlag_Control;
+  mTxInlineFrame[1] = 2;                          /* version */
+  mTxInlineFrame[2] = 0;
+  mTxInlineFrame[3] = SpdySession::CONTROL_TYPE_SYN_STREAM;
+  // 4 to 7 are length and flags, we'll fill that in later
+  
+  PRUint32 networkOrderID = PR_htonl(mStreamID);
+  memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
+  
+  // this is the associated-to field, which is not used sending
+  // from the client in the http binding
+  memset (mTxInlineFrame + 12, 0, 4);
+
+  // Priority flags are the C0 mask of byte 16.
+  // From low to high: 00 40 80 C0
+  // higher raw priority values are actually less important
+  //
+  // The other 6 bits of 16 are unused. Spdy/3 will expand
+  // priority to 4 bits.
+  //
+  // When Spdy/3 implements WINDOW_UPDATE the lowest priority
+  // streams over a threshold (32?) should be given tiny
+  // receive windows, separate from their spdy priority
+  //
+  if (mPriority >= nsISupportsPriority::PRIORITY_LOW)
+    mTxInlineFrame[16] = SpdySession::kPri00;
+  else if (mPriority >= nsISupportsPriority::PRIORITY_NORMAL)
+    mTxInlineFrame[16] = SpdySession::kPri01;
+  else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH)
+    mTxInlineFrame[16] = SpdySession::kPri02;
+  else
+    mTxInlineFrame[16] = SpdySession::kPri03;
+
+  mTxInlineFrame[17] = 0;                         /* unused */
+  
+//  nsCString methodHeader;
+//  mTransaction->RequestHead()->Method()->ToUTF8String(methodHeader);
+  const char *methodHeader = mTransaction->RequestHead()->Method().get();
+
+  nsCString hostHeader;
+  mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
+
+  nsCString versionHeader;
+  if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
+    versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
+  else
+    versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
+
+  nsClassHashtable<nsCStringHashKey, nsCString> hdrHash;
+  
+  // use mRequestHead() to get a sense of how big to make the hash,
+  // even though we are parsing the actual text stream because
+  // it is legit to append headers.
+  hdrHash.Init(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
+  
+  const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
+
+  // need to hash all the headers together to remove duplicates, special
+  // headers, etc..
+
+  PRInt32 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
+  while (true) {
+    PRInt32 startIndex = crlfIndex + 2;
+
+    crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
+    if (crlfIndex == -1)
+      break;
+    
+    PRInt32 colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
+                                                      crlfIndex - startIndex);
+    if (colonIndex == -1)
+      break;
+    
+    nsDependentCSubstring name = Substring(beginBuffer + startIndex,
+                                           beginBuffer + colonIndex);
+    // all header names are lower case in spdy
+    ToLowerCase(name);
+
+    if (name.Equals("method") ||
+        name.Equals("version") ||
+        name.Equals("scheme") ||
+        name.Equals("keep-alive") ||
+        name.Equals("accept-encoding") ||
+        name.Equals("te") ||
+        name.Equals("connection") ||
+        name.Equals("proxy-connection") ||
+        name.Equals("url"))
+      continue;
+    
+    nsCString *val = hdrHash.Get(name);
+    if (!val) {
+      val = new nsCString();
+      hdrHash.Put(name, val);
+    }
+
+    PRInt32 valueIndex = colonIndex + 1;
+    while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
+      ++valueIndex;
+    
+    nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
+                                        beginBuffer + crlfIndex);
+    if (!val->IsEmpty())
+      val->Append(static_cast<char>(0));
+    val->Append(v);
+
+    if (name.Equals("content-length")) {
+      PRInt64 len;
+      if (nsHttp::ParseInt64(val->get(), nsnull, &len))
+        mRequestBodyLen = len;
+    }
+  }
+  
+  mTxInlineFrameSize = 18;
+
+  LOG3(("http request headers to encode are: \n%s",
+        mFlatHttpRequestHeaders.get()));
+
+  // The header block length
+  PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */
+  CompressToFrame(count);
+
+  // method, scheme, url, and version headers for request line
+
+  CompressToFrame(NS_LITERAL_CSTRING("method"));
+  CompressToFrame(methodHeader, strlen(methodHeader));
+  CompressToFrame(NS_LITERAL_CSTRING("scheme"));
+  CompressToFrame(NS_LITERAL_CSTRING("https"));
+  CompressToFrame(NS_LITERAL_CSTRING("url"));
+  CompressToFrame(mTransaction->RequestHead()->RequestURI());
+  CompressToFrame(NS_LITERAL_CSTRING("version"));
+  CompressToFrame(versionHeader);
+  
+  hdrHash.Enumerate(hdrHashEnumerate, this);
+  CompressFlushFrame();
+  
+  // 4 to 7 are length and flags, which we can now fill in
+  (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
+    PR_htonl(mTxInlineFrameSize - 8);
+
+  NS_ABORT_IF_FALSE(!mTxInlineFrame[4],
+                    "Size greater than 24 bits");
+  
+  // For methods other than POST and PUT, we will set the fin bit
+  // right on the syn stream packet.
+
+  if (mTransaction->RequestHead()->Method() != nsHttp::Post &&
+      mTransaction->RequestHead()->Method() != nsHttp::Put) {
+    mSentFinOnData = 1;
+    mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN;
+  }
+
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameSize - 18);
+
+  // The size of the input headers is approximate
+  PRUint32 ratio =
+    (mTxInlineFrameSize - 18) * 100 /
+    (11 + mTransaction->RequestHead()->RequestURI().Length() +
+     mFlatHttpRequestHeaders.Length());
+  
+  Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
+  return NS_OK;
+}
+
+nsresult
+SpdyStream::TransmitFrame(const char *buf,
+                          PRUint32 *countUsed)
+{
+  NS_ABORT_IF_FALSE(mTxInlineFrameSize, "empty stream frame in transmit");
+  NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
+  
+  PRUint32 transmittedCount;
+  nsresult rv;
+  
+  LOG3(("SpdyStream::TransmitFrame %p inline=%d of %d stream=%d of %d",
+        this, mTxInlineFrameSent, mTxInlineFrameSize,
+        mTxStreamFrameSent, mTxStreamFrameSize));
+  if (countUsed)
+    *countUsed = 0;
+  mBlockedOnWrite = 0;
+
+  // In the (relatively common) event that we have a small amount of data
+  // split between the inlineframe and the streamframe, then move the stream
+  // data into the inlineframe via copy in order to coalesce into one write.
+  // Given the interaction with ssl this is worth the small copy cost.
+  if (mTxStreamFrameSize && mTxInlineFrameSize &&
+      !mTxInlineFrameSent && !mTxStreamFrameSent &&
+      mTxStreamFrameSize < SpdySession::kDefaultBufferSize &&
+      mTxInlineFrameSize + mTxStreamFrameSize < mTxInlineFrameAllocation) {
+    LOG3(("Coalesce Transmit"));
+    memcpy (mTxInlineFrame + mTxInlineFrameSize,
+            buf, mTxStreamFrameSize);
+    mTxInlineFrameSize += mTxStreamFrameSize;
+    mTxStreamFrameSent = 0;
+    mTxStreamFrameSize = 0;
+  }
+
+  // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
+  // bytes through to the SpdySession and then the HttpConnection which calls
+  // the socket write function.
+  
+  while (mTxInlineFrameSent < mTxInlineFrameSize) {
+    rv = mSegmentReader->OnReadSegment(mTxInlineFrame + mTxInlineFrameSent,
+                                       mTxInlineFrameSize - mTxInlineFrameSent,
+                                       &transmittedCount);
+    LOG3(("SpdyStream::TransmitFrame for inline session=%p "
+          "stream=%p result %x len=%d",
+          mSession, this, rv, transmittedCount));
+    SpdySession::LogIO(mSession, this, "Writing from Inline Buffer",
+                       mTxInlineFrame + mTxInlineFrameSent,
+                       transmittedCount);
+
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+      mBlockedOnWrite = 1;
+
+    if (NS_FAILED(rv))     // this will include WOULD_BLOCK
+      return rv;
+    
+    mTxInlineFrameSent += transmittedCount;
+  }
+
+  PRUint32 offset = 0;
+  NS_ABORT_IF_FALSE(mTxStreamFrameSize >= mTxStreamFrameSent,
+                    "negative unsent");
+  PRUint32 avail =  mTxStreamFrameSize - mTxStreamFrameSent;
+
+  while (avail) {
+    NS_ABORT_IF_FALSE(countUsed, "null countused pointer in a stream context");
+    rv = mSegmentReader->OnReadSegment(buf + offset, avail, &transmittedCount);
+
+    LOG3(("SpdyStream::TransmitFrame for regular session=%p "
+          "stream=%p result %x len=%d",
+          mSession, this, rv, transmittedCount));
+    SpdySession::LogIO(mSession, this, "Writing from Transaction Buffer",
+                       buf + offset, transmittedCount);
+
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+      mBlockedOnWrite = 1;
+
+    if (NS_FAILED(rv))     // this will include WOULD_BLOCK
+      return rv;
+    
+    if (mUpstreamState == SENDING_REQUEST_BODY) {
+      mTransaction->OnTransportStatus(mSocketTransport,
+                                      nsISocketTransport::STATUS_SENDING_TO,
+                                      transmittedCount);
+    }
+    
+    *countUsed += transmittedCount;
+    avail -= transmittedCount;
+    offset += transmittedCount;
+    mTxStreamFrameSent += transmittedCount;
+  }
+
+  if (!avail) {
+    mTxInlineFrameSent = 0;
+    mTxInlineFrameSize = 0;
+    mTxStreamFrameSent = 0;
+    mTxStreamFrameSize = 0;
+  }
+    
+  return NS_OK;
+}
+
+void
+SpdyStream::ChangeState(enum stateType newState)
+{
+  LOG3(("SpdyStream::ChangeState() %p from %X to %X",
+        this, mUpstreamState, newState));
+  mUpstreamState = newState;
+  return;
+}
+
+void
+SpdyStream::GenerateDataFrameHeader(PRUint32 dataLength, bool lastFrame)
+{
+  LOG3(("SpdyStream::GenerateDataFrameHeader %p len=%d last=%d",
+        this, dataLength, lastFrame));
+
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(!mTxInlineFrameSize, "inline frame not empty");
+  NS_ABORT_IF_FALSE(!mTxInlineFrameSent, "inline partial send not 0");
+  NS_ABORT_IF_FALSE(!mTxStreamFrameSize, "stream frame not empty");
+  NS_ABORT_IF_FALSE(!mTxStreamFrameSent, "stream partial send not 0");
+  NS_ABORT_IF_FALSE(!(dataLength & 0xff000000), "datalength > 24 bits");
+  
+  (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
+  (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
+    PR_htonl(dataLength);
+  
+  NS_ABORT_IF_FALSE(!(mTxInlineFrame[0] & 0x80),
+                    "control bit set unexpectedly");
+  NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "flag bits set unexpectedly");
+  
+  mTxInlineFrameSize = 8;
+  mTxStreamFrameSize = dataLength;
+
+  if (lastFrame) {
+    mTxInlineFrame[4] |= SpdySession::kFlag_Data_FIN;
+    if (dataLength)
+      mSentFinOnData = 1;
+  }
+}
+
+void
+SpdyStream::CompressToFrame(const nsACString &str)
+{
+  CompressToFrame(str.BeginReading(), str.Length());
+}
+
+void
+SpdyStream::CompressToFrame(const nsACString *str)
+{
+  CompressToFrame(str->BeginReading(), str->Length());
+}
+
+// Dictionary taken from
+// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2 
+// Name/Value Header Block Format
+// spec indicates that the compression dictionary is not null terminated
+// but in reality it is. see:
+// https://groups.google.com/forum/#!topic/spdy-dev/2pWxxOZEIcs
+
+const char *SpdyStream::kDictionary =
+  "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+  "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+  "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+  "-agent10010120020120220320420520630030130230330430530630740040140240340440"
+  "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+  "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+  "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+  "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+  "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+  "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+  "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+  "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+  ".1statusversionurl";
+
+// use for zlib data types
+void *
+SpdyStream::zlib_allocator(void *opaque, uInt items, uInt size)
+{
+  return moz_xmalloc(items * size);
+}
+
+// use for zlib data types
+void
+SpdyStream::zlib_destructor(void *opaque, void *addr)
+{
+  moz_free(addr);
+}
+
+void
+SpdyStream::ExecuteCompress(PRUint32 flushMode)
+{
+  // Expect mZlib->avail_in and mZlib->next_in to be set.
+  // Append the compressed version of next_in to mTxInlineFrame
+
+  do
+  {
+    PRUint32 avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
+    if (avail < 1) {
+      SpdySession::EnsureBuffer(mTxInlineFrame,
+                                mTxInlineFrameAllocation + 2000,
+                                mTxInlineFrameSize,
+                                mTxInlineFrameAllocation);
+      avail = mTxInlineFrameAllocation - mTxInlineFrameSize;
+    }
+
+    mZlib->next_out = reinterpret_cast<unsigned char *> (mTxInlineFrame.get()) +
+      mTxInlineFrameSize;
+    mZlib->avail_out = avail;
+    deflate(mZlib, flushMode);
+    mTxInlineFrameSize += avail - mZlib->avail_out;
+  } while (mZlib->avail_in > 0 || !mZlib->avail_out);
+}
+
+void
+SpdyStream::CompressToFrame(PRUint16 data)
+{
+  // convert the data to network byte order and write that
+  // to the compressed stream
+  
+  data = PR_htons(data);
+
+  mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
+  mZlib->avail_in = 2;
+  ExecuteCompress(Z_NO_FLUSH);
+}
+
+
+void
+SpdyStream::CompressToFrame(const char *data, PRUint32 len)
+{
+  // Format calls for a network ordered 16 bit length
+  // followed by the utf8 string
+
+  // for now, silently truncate headers greater than 64KB. Spdy/3 will
+  // fix this by making the len a 32 bit quantity
+  if (len > 0xffff)
+    len = 0xffff;
+
+  PRUint16 networkLen = len;
+  networkLen = PR_htons(len);
+  
+  // write out the length
+  mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
+  mZlib->avail_in = 2;
+  ExecuteCompress(Z_NO_FLUSH);
+  
+  // write out the data
+  mZlib->next_in = (unsigned char *)data;
+  mZlib->avail_in = len;
+  ExecuteCompress(Z_NO_FLUSH);
+}
+
+void
+SpdyStream::CompressFlushFrame()
+{
+  mZlib->next_in = (unsigned char *) "";
+  mZlib->avail_in = 0;
+  ExecuteCompress(Z_SYNC_FLUSH);
+}
+
+void
+SpdyStream::Close(nsresult reason)
+{
+  mTransaction->Close(reason);
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentReader
+//-----------------------------------------------------------------------------
+
+nsresult
+SpdyStream::OnReadSegment(const char *buf,
+                          PRUint32 count,
+                          PRUint32 *countRead)
+{
+  LOG3(("SpdyStream::OnReadSegment %p count=%d state=%x",
+        this, count, mUpstreamState));
+
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(mSegmentReader, "OnReadSegment with null mSegmentReader");
+  
+  nsresult rv = NS_ERROR_UNEXPECTED;
+  PRUint32 dataLength;
+
+  switch (mUpstreamState) {
+  case GENERATING_SYN_STREAM:
+    // The buffer is the HTTP request stream, including at least part of the
+    // HTTP request header. This state's job is to build a SYN_STREAM frame
+    // from the header information. count is the number of http bytes available
+    // (which may include more than the header), and in countRead we return
+    // the number of those bytes that we consume (i.e. the portion that are
+    // header bytes)
+
+    rv = ParseHttpRequestHeaders(buf, count, countRead);
+    if (NS_FAILED(rv))
+      return rv;
+    LOG3(("ParseHttpRequestHeaders %p used %d of %d.",
+          this, *countRead, count));
+    if (mSynFrameComplete) {
+      NS_ABORT_IF_FALSE(mTxInlineFrameSize,
+                        "OnReadSegment SynFrameComplete 0b");
+      rv = TransmitFrame(nsnull, nsnull);
+      if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
+        rv = NS_OK;
+      if (mTxInlineFrameSize)
+        ChangeState(SENDING_SYN_STREAM);
+      else
+        ChangeState(GENERATING_REQUEST_BODY);
+      break;
+    }
+    NS_ABORT_IF_FALSE(*countRead == count,
+                      "Header parsing not complete but unused data");
+    break;
+
+  case GENERATING_REQUEST_BODY:
+    NS_ABORT_IF_FALSE(!mTxInlineFrameSent,
+                      "OnReadSegment in generating_request_body with "
+                      "frame in progress");
+    if (count < mChunkSize && count < mRequestBodyLen) {
+      LOG3(("SpdyStream %p id %x has %d to write out of a bodylen %d"
+            " with a chunk size of %d. Waiting for more.",
+            this, mStreamID, count, mChunkSize, mRequestBodyLen));
+      rv = NS_BASE_STREAM_WOULD_BLOCK;
+      break;
+    }
+    
+    dataLength = NS_MIN(count, mChunkSize);
+    if (dataLength > mRequestBodyLen)
+      return NS_ERROR_UNEXPECTED;
+    mRequestBodyLen -= dataLength;
+    GenerateDataFrameHeader(dataLength, !mRequestBodyLen);
+    ChangeState(SENDING_REQUEST_BODY);
+    // NO BREAK
+
+  case SENDING_REQUEST_BODY:
+    NS_ABORT_IF_FALSE(mTxInlineFrameSize, "OnReadSegment Send Data Header 0b");
+    rv = TransmitFrame(buf, countRead);
+    LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
+          "Header is %d/%d Body is %d/%d.",
+          rv, *countRead,
+          mTxInlineFrameSent, mTxInlineFrameSize,
+          mTxStreamFrameSent, mTxStreamFrameSize));
+
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
+      rv = NS_OK;
+
+    // If that frame was all sent, look for another one
+    if (!mTxInlineFrameSize)
+        ChangeState(GENERATING_REQUEST_BODY);
+    break;
+
+  case SENDING_SYN_STREAM:
+    rv = NS_BASE_STREAM_WOULD_BLOCK;
+    break;
+
+  case SENDING_FIN_STREAM:
+    NS_ABORT_IF_FALSE(false,
+                      "resuming partial fin stream out of OnReadSegment");
+    break;
+    
+  default:
+    NS_ABORT_IF_FALSE(false, "SpdyStream::OnReadSegment non-write state");
+    break;
+  }
+  
+  return rv;
+}
+
+//-----------------------------------------------------------------------------
+// nsAHttpSegmentWriter
+//-----------------------------------------------------------------------------
+
+nsresult
+SpdyStream::OnWriteSegment(char *buf,
+                           PRUint32 count,
+                           PRUint32 *countWritten)
+{
+  LOG3(("SpdyStream::OnWriteSegment %p count=%d state=%x",
+        this, count, mUpstreamState));
+
+  NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+  NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
+
+  return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/SpdyStream.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* ***** 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.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 mozilla_net_SpdyStream_h
+#define mozilla_net_SpdyStream_h
+
+#include "nsAHttpTransaction.h"
+
+namespace mozilla { namespace net {
+
+class SpdyStream : public nsAHttpSegmentReader
+                 , public nsAHttpSegmentWriter
+{
+public:
+  NS_DECL_NSAHTTPSEGMENTREADER
+  NS_DECL_NSAHTTPSEGMENTWRITER
+
+  SpdyStream(nsAHttpTransaction *,
+             SpdySession *, nsISocketTransport *,
+             PRUint32, z_stream *, PRInt32);
+  ~SpdyStream();
+
+  PRUint32 StreamID() { return mStreamID; }
+
+  nsresult ReadSegments(nsAHttpSegmentReader *,  PRUint32, PRUint32 *);
+  nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
+
+  bool BlockedOnWrite()
+  {
+    return static_cast<bool>(mBlockedOnWrite);
+  }
+
+  bool RequestBlockedOnRead()
+  {
+    return static_cast<bool>(mRequestBlockedOnRead);
+  }
+
+  // returns false if called more than once
+  bool SetFullyOpen()
+  {
+    bool result = !mFullyOpen;
+    mFullyOpen = 1;
+    return result;
+  }
+
+  nsAHttpTransaction *Transaction()
+  {
+    return mTransaction;
+  }
+
+  void Close(nsresult reason);
+
+  void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
+  bool RecvdFin() { return mRecvdFin; }
+
+  // The zlib header compression dictionary defined by SPDY,
+  // and hooks to the mozilla allocator for zlib to use.
+  static const char *kDictionary;
+  static void *zlib_allocator(void *, uInt, uInt);
+  static void zlib_destructor(void *, void *);
+
+private:
+
+  enum stateType {
+    GENERATING_SYN_STREAM,
+    SENDING_SYN_STREAM,
+    GENERATING_REQUEST_BODY,
+    SENDING_REQUEST_BODY,
+    SENDING_FIN_STREAM,
+    UPSTREAM_COMPLETE
+  };
+
+  static PLDHashOperator hdrHashEnumerate(const nsACString &,
+                                          nsAutoPtr<nsCString> &,
+                                          void *);
+
+  void     ChangeState(enum stateType );
+  nsresult ParseHttpRequestHeaders(const char *, PRUint32, PRUint32 *);
+  nsresult TransmitFrame(const char *, PRUint32 *);
+  void     GenerateDataFrameHeader(PRUint32, bool);
+
+  void     CompressToFrame(const nsACString &);
+  void     CompressToFrame(const nsACString *);
+  void     CompressToFrame(const char *, PRUint32);
+  void     CompressToFrame(PRUint16);
+  void     CompressFlushFrame();
+  void     ExecuteCompress(PRUint32);
+  
+  // Each stream goes from syn_stream to upstream_complete, perhaps
+  // looping on multiple instances of generating_request_body and
+  // sending_request_body for each SPDY chunk in the upload.
+  enum stateType mUpstreamState;
+
+  // The underlying HTTP transaction
+  nsRefPtr<nsAHttpTransaction> mTransaction;
+
+  // The session that this stream is a subset of
+  SpdySession                *mSession;
+
+  // The underlying socket transport object is needed to propogate some events
+  nsISocketTransport         *mSocketTransport;
+
+  // These are temporary state variables to hold the argument to
+  // Read/WriteSegments so it can be accessed by On(read/write)segment
+  // further up the stack.
+  nsAHttpSegmentReader        *mSegmentReader;
+  nsAHttpSegmentWriter        *mSegmentWriter;
+
+  // The 24 bit SPDY stream ID
+  PRUint32                    mStreamID;
+
+  // The quanta upstream data frames are chopped into
+  PRUint32                    mChunkSize;
+
+  // Flag is set when all http request headers have been read
+  PRUint32                     mSynFrameComplete     : 1;
+
+  // Flag is set when there is more request data to send and the
+  // stream needs to be rescheduled for writing. Sometimes this
+  // is done as a matter of fairness, not really due to blocking
+  PRUint32                     mBlockedOnWrite       : 1;
+
+  // Flag is set when the HTTP processor has more data to send
+  // but has blocked in doing so.
+  PRUint32                     mRequestBlockedOnRead : 1;
+
+  // Flag is set when a FIN has been placed on a data or syn packet
+  // (i.e after the client has closed)
+  PRUint32                     mSentFinOnData        : 1;
+
+  // Flag is set after the response frame bearing the fin bit has
+  // been processed. (i.e. after the server has closed).
+  PRUint32                     mRecvdFin             : 1;
+
+  // Flag is set after syn reply received
+  PRUint32                     mFullyOpen            : 1;
+
+  // The InlineFrame and associated data is used for composing control
+  // frames and data frame headers.
+  nsAutoArrayPtr<char>         mTxInlineFrame;
+  PRUint32                     mTxInlineFrameAllocation;
+  PRUint32                     mTxInlineFrameSize;
+  PRUint32                     mTxInlineFrameSent;
+
+  // mTxStreamFrameSize and mTxStreamFrameSent track the progress of
+  // transmitting a request body data frame. The data frame itself
+  // is never copied into the spdy layer.
+  PRUint32                     mTxStreamFrameSize;
+  PRUint32                     mTxStreamFrameSent;
+
+  // Compression context and buffer for request header compression.
+  // This is a copy of SpdySession::mUpstreamZlib because it needs
+  //  to remain the same in all streams of a session.
+  z_stream                     *mZlib;
+  nsCString                    mFlatHttpRequestHeaders;
+
+  // Track the content-length of a request body so that we can
+  // place the fin flag on the last data packet instead of waiting
+  // for a stream closed indication. Relying on stream close results
+  // in an extra 0-length runt packet and seems to have some interop
+  // problems with the google servers.
+  PRInt64                      mRequestBodyLen;
+
+  // based on nsISupportsPriority definitions
+  PRInt32                      mPriority;
+
+};
+
+}} // namespace mozilla::net
+
+#endif // mozilla_net_SpdyStream_h
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -72,18 +72,18 @@ public:
                                         nsHttpResponseHead *,
                                         bool *reset) = 0;
 
     //
     // called by a transaction to resume either sending or receiving data
     // after a transaction returned NS_BASE_STREAM_WOULD_BLOCK from its
     // ReadSegments/WriteSegments methods.
     //
-    virtual nsresult ResumeSend() = 0;
-    virtual nsresult ResumeRecv() = 0;
+    virtual nsresult ResumeSend(nsAHttpTransaction *caller) = 0;
+    virtual nsresult ResumeRecv(nsAHttpTransaction *caller) = 0;
 
     //
     // called by the connection manager to close a transaction being processed
     // by this connection.
     //
     // @param transaction
     //        the transaction being closed.
     // @param reason
@@ -127,18 +127,18 @@ public:
 
     // Get the nsISocketTransport used by the connection without changing
     //  references or ownership.
     virtual nsISocketTransport *Transport() = 0;
 };
 
 #define NS_DECL_NSAHTTPCONNECTION \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
-    nsresult ResumeSend(); \
-    nsresult ResumeRecv(); \
+    nsresult ResumeSend(nsAHttpTransaction *); \
+    nsresult ResumeRecv(nsAHttpTransaction *); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     void GetConnectionInfo(nsHttpConnectionInfo **); \
     nsresult TakeTransport(nsISocketTransport **,    \
                            nsIAsyncInputStream **,   \
                            nsIAsyncOutputStream **); \
     void GetSecurityInfo(nsISupports **); \
     bool IsPersistent(); \
     bool IsReused(); \
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -57,16 +57,17 @@ class nsHttpRequestHead;
 // write function returns NS_BASE_STREAM_WOULD_BLOCK in this case).
 //----------------------------------------------------------------------------
 
 class nsAHttpTransaction : public nsISupports
 {
 public:
     // called by the connection when it takes ownership of the transaction.
     virtual void SetConnection(nsAHttpConnection *) = 0;
+    virtual nsAHttpConnection *Connection() = 0;
 
     // called by the connection to get security callbacks to set on the
     // socket transport.
     virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
                                       nsIEventTarget **) = 0;
 
     // called to report socket status (see nsITransportEventSink)
     virtual void OnTransportStatus(nsITransport* transport,
@@ -94,16 +95,17 @@ public:
     virtual void SetSSLConnectFailed() = 0;
     
     // called to retrieve the request headers of the transaction
     virtual nsHttpRequestHead *RequestHead() = 0;
 };
 
 #define NS_DECL_NSAHTTPTRANSACTION \
     void SetConnection(nsAHttpConnection *); \
+    nsAHttpConnection *Connection(); \
     void GetSecurityCallbacks(nsIInterfaceRequestor **, \
                               nsIEventTarget **);       \
     void OnTransportStatus(nsITransport* transport, \
                            nsresult status, PRUint64 progress); \
     bool     IsDone(); \
     nsresult Status(); \
     PRUint32 Available(); \
     nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -126,16 +126,21 @@ typedef PRUint8 nsHttpVersion;
 // a transaction with this caps flag keeps timing information
 #define NS_HTTP_TIMING_ENABLED       (1<<5)
 
 // a transaction with this caps flag will not only not use an existing
 // persistent connection but it will close outstanding ones to the same
 // host. Used by a forced reload to reset the connection states.
 #define NS_HTTP_CLEAR_KEEPALIVES     (1<<6)
 
+// Disallow the use of the SPDY protocol. This is meant for the contexts
+// such as HTTP upgrade which are nonsensical for SPDY, it is not the
+// SPDY configuration variable.
+#define NS_HTTP_DISALLOW_SPDY        (1<<7)
+
 //-----------------------------------------------------------------------------
 // some default values
 //-----------------------------------------------------------------------------
 
 // hard upper limit on the number of requests that can be pipelined
 #define NS_HTTP_MAX_PIPELINED_REQUESTS 8 
 
 #define NS_HTTP_DEFAULT_PORT  80
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -51,16 +51,17 @@
  ******/
 
 HTTP_ATOM(Accept,                    "Accept")
 HTTP_ATOM(Accept_Encoding,           "Accept-Encoding")
 HTTP_ATOM(Accept_Language,           "Accept-Language")
 HTTP_ATOM(Accept_Ranges,             "Accept-Ranges")
 HTTP_ATOM(Age,                       "Age")
 HTTP_ATOM(Allow,                     "Allow")
+HTTP_ATOM(Alternate_Protocol,        "Alternate-Protocol")
 HTTP_ATOM(Authentication,            "Authentication")
 HTTP_ATOM(Authorization,             "Authorization")
 HTTP_ATOM(Cache_Control,             "Cache-Control")
 HTTP_ATOM(Connection,                "Connection")
 HTTP_ATOM(Content_Base,              "Content-Base")
 HTTP_ATOM(Content_Disposition,       "Content-Disposition")
 HTTP_ATOM(Content_Encoding,          "Content-Encoding")
 HTTP_ATOM(Content_Language,          "Content-Language")
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -204,16 +204,28 @@ nsHttpChannel::Connect(bool firstTime)
         // worrisome.
         NS_ASSERTION(NS_SUCCEEDED(rv),
                      "Something is wrong with STS: IsStsURI failed.");
 
         if (NS_SUCCEEDED(rv) && isStsHost) {
             LOG(("nsHttpChannel::Connect() STS permissions found\n"));
             return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
         }
+
+        // Check for a previous SPDY Alternate-Protocol directive
+        if (gHttpHandler->IsSpdyEnabled() && mAllowSpdy) {
+            nsCAutoString hostPort;
+
+            if (NS_SUCCEEDED(mURI->GetHostPort(hostPort)) &&
+                gHttpHandler->ConnMgr()->GetSpdyAlternateProtocol(hostPort)) {
+                LOG(("nsHttpChannel::Connect() Alternate-Protocol found\n"));
+                return AsyncCall(
+                    &nsHttpChannel::HandleAsyncRedirectChannelToHttps);
+            }
+        }
     }
 
     // ensure that we are using a valid hostname
     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
         return NS_ERROR_UNKNOWN_HOST;
 
     // true when called from AsyncOpen
     if (firstTime) {
@@ -502,16 +514,19 @@ nsHttpChannel::SetupTransaction()
               mRequestHead.Method() == nsHttp::Head ||
               mRequestHead.Method() == nsHttp::Propfind ||
               mRequestHead.Method() == nsHttp::Proppatch)) {
             LOG(("  pipelining disallowed\n"));
             mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         }
     }
 
+    if (!mAllowSpdy)
+        mCaps |= NS_HTTP_DISALLOW_SPDY;
+
     // use the URI path if not proxying (transparent proxying such as SSL proxy
     // does not count here). also, figure out what version we should be speaking.
     nsCAutoString buf, path;
     nsCString* requestURI;
     if (mConnectionInfo->UsingSSL() ||
         mConnectionInfo->ShouldForceConnectMethod() ||
         !mConnectionInfo->UsingHttpProxy()) {
         rv = mURI->GetPath(path);
@@ -629,16 +644,17 @@ nsHttpChannel::SetupTransaction()
     if (mUpgradeProtocolCallback) {
         mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
         mRequestHead.SetHeader(nsHttp::Connection,
                                nsDependentCString(nsHttp::Upgrade.get()),
                                true);
         mCaps |=  NS_HTTP_STICKY_CONNECTION;
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
+        mCaps |=  NS_HTTP_DISALLOW_SPDY;
     }
 
     nsCOMPtr<nsIAsyncInputStream> responseStream;
     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
                             mUploadStream, mUploadStreamHasHeaders,
                             NS_GetCurrentThread(), callbacks, this,
                             getter_AddRefs(responseStream));
     if (NS_FAILED(rv)) {
@@ -4085,16 +4101,26 @@ nsHttpChannel::OnStartRequest(nsIRequest
                  "If we have both pumps, the cache content must be partial");
 
     if (!mSecurityInfo && !mCachePump && mTransaction) {
         // grab the security info from the connection object; the transaction
         // is guaranteed to own a reference to the connection.
         mSecurityInfo = mTransaction->SecurityInfo();
     }
 
+    if (gHttpHandler->IsSpdyEnabled() && !mCachePump && NS_FAILED(mStatus) &&
+        (mLoadFlags & LOAD_REPLACE) && mOriginalURI && mAllowSpdy) {
+        // For sanity's sake we may want to cancel an alternate protocol
+        // redirection involving the original host name
+
+        nsCAutoString hostPort;
+        if (NS_SUCCEEDED(mOriginalURI->GetHostPort(hostPort)))
+            gHttpHandler->ConnMgr()->RemoveSpdyAlternateProtocol(hostPort);
+    }
+
     // don't enter this block if we're reading from the cache...
     if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
         // mTransactionPump doesn't hit OnInputStreamReady and call this until
         // all of the response headers have been acquired, so we can take ownership
         // of them from the transaction.
         mResponseHead = mTransaction->TakeResponseHead();
         // the response head may be null if the transaction was cancelled.  in
         // which case we just need to call OnStartRequest/OnStopRequest.
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -48,16 +48,19 @@
 #include "nsIServiceManager.h"
 #include "nsISSLSocketControl.h"
 #include "nsStringStream.h"
 #include "netCore.h"
 #include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "prmem.h"
 #include "nsPreloadedStream.h"
+#include "SpdySession.h"
+#include "mozilla/Telemetry.h"
+#include "nsISupportsPriority.h"
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 #endif
 
 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
 
@@ -77,16 +80,21 @@ nsHttpConnection::nsHttpConnection()
     , mMaxBytesRead(0)
     , mKeepAlive(true) // assume to keep-alive by default
     , mKeepAliveMask(true)
     , mSupportsPipelining(false) // assume low-grade server
     , mIsReused(false)
     , mCompletedProxyConnect(false)
     , mLastTransactionExpectedNoContent(false)
     , mIdleMonitoring(false)
+    , mNPNComplete(false)
+    , mSetupNPNCalled(false)
+    , mUsingSpdy(false)
+    , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
+    , mReportedSpdy(false)
 {
     LOG(("Creating nsHttpConnection @%x\n", this));
 
     // grab a reference to the handler to ensure that it doesn't go away.
     nsHttpHandler *handler = gHttpHandler;
     NS_ADDREF(handler);
 }
 
@@ -136,41 +144,117 @@ nsHttpConnection::Init(nsHttpConnectionI
     mCallbacks = callbacks;
     mCallbackTarget = callbackTarget;
     rv = mSocketTransport->SetSecurityCallbacks(this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
+bool
+nsHttpConnection::EnsureNPNComplete()
+{
+    // NPN is only used by SPDY right now.
+    //
+    // If for some reason the components to check on NPN aren't available,
+    // this function will just return true to continue on and disable SPDY
+
+    NS_ABORT_IF_FALSE(mSocketTransport, "EnsureNPNComplete "
+                      "socket transport precondition");
+
+    if (mNPNComplete)
+        return true;
+    
+    nsresult rv;
+
+    nsCOMPtr<nsISupports> securityInfo;
+    nsCOMPtr<nsISSLSocketControl> ssl;
+    nsCAutoString negotiatedNPN;
+    
+    rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
+    if (NS_FAILED(rv))
+        goto npnComplete;
+
+    ssl = do_QueryInterface(securityInfo, &rv);
+    if (NS_FAILED(rv))
+        goto npnComplete;
+
+    rv = ssl->GetNegotiatedNPN(negotiatedNPN);
+    if (rv == NS_ERROR_NOT_CONNECTED) {
+    
+        // By writing 0 bytes to the socket the SSL handshake machine is
+        // pushed forward.
+        PRUint32 count = 0;
+        rv = mSocketOut->Write("", 0, &count);
+
+        if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
+            goto npnComplete;
+        return false;
+    }
+    
+    if (NS_FAILED(rv))
+        goto npnComplete;
+
+    LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'",
+         this, negotiatedNPN.get()));
+    
+    if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) {
+
+        mUsingSpdy = true;
+        mIsReused = true;    /* all spdy streams are reused */
+
+        // Wrap the old http transaction into the new spdy session
+        // as the first stream
+        mSpdySession = new SpdySession(mTransaction,
+                                       mSocketTransport,
+                                       mPriority);
+        mTransaction = mSpdySession;
+        mIdleTimeout = gHttpHandler->SpdyTimeout();
+    }
+
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_CONNECT,
+                                   mUsingSpdy);
+
+npnComplete:
+    LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true"));
+    mNPNComplete = true;
+    return true;
+}
+
 // called on the socket thread
 nsresult
-nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
+nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps, PRInt32 pri)
 {
     nsresult rv;
 
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
          this, trans, caps));
 
+    mPriority = pri;
+    if (mTransaction && mUsingSpdy)
+        return AddTransaction(trans, pri);
+
     NS_ENSURE_ARG_POINTER(trans);
     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
 
     // Update security callbacks
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     nsCOMPtr<nsIEventTarget>        callbackTarget;
     trans->GetSecurityCallbacks(getter_AddRefs(callbacks),
                                 getter_AddRefs(callbackTarget));
     if (callbacks != mCallbacks) {
         mCallbacks.swap(callbacks);
         if (callbacks)
             NS_ProxyRelease(mCallbackTarget, callbacks);
         mCallbackTarget = callbackTarget;
     }
 
+    SetupNPN(caps); // only for spdy
+
     // take ownership of the transaction
     mTransaction = trans;
 
     NS_ABORT_IF_FALSE(!mIdleMonitoring,
                       "Activating a connection with an Idle Monitor");
     mIdleMonitoring = false;
 
     // set mKeepAlive according to what will be requested
@@ -194,16 +278,105 @@ failed_activation:
     if (NS_FAILED(rv)) {
         mTransaction = nsnull;
     }
 
     return rv;
 }
 
 void
+nsHttpConnection::SetupNPN(PRUint8 caps)
+{
+    if (mSetupNPNCalled)                                /* do only once */
+        return;
+    mSetupNPNCalled = true;
+
+    // Setup NPN Negotiation if necessary (only for SPDY)
+    if (!mNPNComplete) {
+
+        mNPNComplete = true;
+
+        if (mConnInfo->UsingSSL() &&
+            !(caps & NS_HTTP_DISALLOW_SPDY) &&
+            !mConnInfo->UsingHttpProxy() &&
+            gHttpHandler->IsSpdyEnabled()) {
+            LOG(("nsHttpConnection::Init Setting up SPDY Negotiation"));
+            nsCOMPtr<nsISupports> securityInfo;
+            nsresult rv =
+                mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
+            if (NS_FAILED(rv))
+                return;
+
+            nsCOMPtr<nsISSLSocketControl> ssl =
+                do_QueryInterface(securityInfo, &rv);
+            if (NS_FAILED(rv))
+                return;
+
+            nsTArray<nsCString> protocolArray;
+            protocolArray.AppendElement(NS_LITERAL_CSTRING("spdy/2"));
+            protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
+            if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
+                LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
+                mNPNComplete = false;
+            }
+        }
+    }
+}
+
+void
+nsHttpConnection::HandleAlternateProtocol(nsHttpResponseHead *responseHead)
+{
+    // Look for the Alternate-Protocol header. Alternate-Protocol is
+    // essentially a way to rediect future transactions from http to
+    // spdy.
+    //
+
+    if (!gHttpHandler->IsSpdyEnabled() || mUsingSpdy)
+        return;
+
+    const char *val = responseHead->PeekHeader(nsHttp::Alternate_Protocol);
+    if (!val)
+        return;
+
+    // The spec allows redirections to any port, but due to concerns over
+    // silently redirecting to stealth ports we only allow port 443
+    //
+    // Alternate-Protocol: 5678:somethingelse, 443:npn-spdy/2
+
+    if (nsHttp::FindToken(val, "443:npn-spdy/2", HTTP_HEADER_VALUE_SEPS)) {
+        LOG(("Connection %p Transaction %p found Alternate-Protocol "
+             "header %s", this, mTransaction.get(), val));
+        gHttpHandler->ConnMgr()->ReportSpdyAlternateProtocol(this);
+    }
+}
+
+nsresult
+nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
+                                 PRInt32 priority)
+{
+    LOG(("nsHttpConnection::AddTransaction for SPDY"));
+
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    NS_ABORT_IF_FALSE(mSpdySession && mUsingSpdy,
+                      "AddTransaction to live http connection without spdy");
+    NS_ABORT_IF_FALSE(mTransaction,
+                      "AddTransaction to idle http connection");
+    
+    if (!mSpdySession->AddStream(httpTransaction, priority)) {
+        NS_ABORT_IF_FALSE(0, "AddStream should never fail due to"
+                          "RoomForMore() admission check");
+        return NS_ERROR_FAILURE;
+    }
+
+    ResumeSend(httpTransaction);
+
+    return NS_OK;
+}
+
+void
 nsHttpConnection::Close(nsresult reason)
 {
     LOG(("nsHttpConnection::Close [this=%x reason=%x]\n", this, reason));
 
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (NS_FAILED(reason)) {
         if (mIdleMonitoring)
@@ -232,55 +405,86 @@ nsHttpConnection::ProxyStartSSL()
     if (NS_FAILED(rv)) return rv;
 
     nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
     if (NS_FAILED(rv)) return rv;
 
     return ssl->ProxyStartSSL();
 }
 
+void
+nsHttpConnection::DontReuse()
+{
+    mKeepAliveMask = false;
+    mKeepAlive = false;
+    mIdleTimeout = 0;
+    if (mUsingSpdy)
+        mSpdySession->DontReuse();
+}
+
 bool
 nsHttpConnection::CanReuse()
 {
-    bool canReuse = IsKeepAlive() &&
+    bool canReuse;
+    
+    if (mUsingSpdy)
+        canReuse = mSpdySession->CanReuse();
+    else
+        canReuse = IsKeepAlive();
+    
+    canReuse = canReuse &&
         (NowInSeconds() - mLastReadTime < mIdleTimeout) &&
         IsAlive();
-    
+
     // An idle persistent connection should not have data waiting to be read
     // before a request is sent. Data here is likely a 408 timeout response
     // which we would deal with later on through the restart logic, but that
     // path is more expensive than just closing the socket now. SSL check can
     // be removed with fixing of 631801
 
     PRUint32 dataSize;
-    if (canReuse && mSocketIn && !mConnInfo->UsingSSL() &&
+    if (canReuse && mSocketIn && !mConnInfo->UsingSSL() && !mUsingSpdy &&
         NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
         LOG(("nsHttpConnection::CanReuse %p %s"
              "Socket not reusable because read data pending (%d) on it.\n",
              this, mConnInfo->Host(), dataSize));
         canReuse = false;
     }
     return canReuse;
 }
 
+bool
+nsHttpConnection::CanDirectlyActivate()
+{
+    // return true if a new transaction can be addded to ths connection at any
+    // time through Activate(). In practice this means this is a healthy SPDY
+    // connection with room for more concurrent streams.
+    
+    return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
+}
+
 PRUint32 nsHttpConnection::TimeToLive()
 {
     PRInt32 tmp = mIdleTimeout - (NowInSeconds() - mLastReadTime);
     if (0 > tmp)
         tmp = 0;
 
     return tmp;
 }
 
 bool
 nsHttpConnection::IsAlive()
 {
     if (!mSocketTransport)
         return false;
 
+    // SocketTransport::IsAlive can run the SSL state machine, so make sure
+    // the NPN options are set before that happens.
+    SetupNPN(0);
+
     bool alive;
     nsresult rv = mSocketTransport->IsAlive(&alive);
     if (NS_FAILED(rv))
         alive = false;
 
 //#define TEST_RESTART_LOGIC
 #ifdef TEST_RESTART_LOGIC
     if (!alive) {
@@ -290,16 +494,20 @@ nsHttpConnection::IsAlive()
 #endif
 
     return alive;
 }
 
 bool
 nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
 {
+    // SPDY supports infinite parallelism, so no need to pipeline.
+    if (mUsingSpdy)
+        return false;
+
     // XXX there should be a strict mode available that disables this
     // blacklisting.
 
     // assuming connection is HTTP/1.1 with keep-alive enabled
     if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
         // XXX check for bad proxy servers...
         return true;
     }
@@ -415,30 +623,40 @@ nsHttpConnection::OnHeadersAvailable(nsA
     // reused as well as the maximum amount of time the connection can be idle
     // before the server will close it.  we ignore the max reuse count, because
     // a "keep-alive" connection is by definition capable of being reused, and
     // we only care about being able to reuse it once.  if a timeout is not 
     // specified then we use our advertized timeout value.
     if (mKeepAlive) {
         val = responseHead->PeekHeader(nsHttp::Keep_Alive);
 
-        const char *cp = PL_strcasestr(val, "timeout=");
-        if (cp)
-            mIdleTimeout = (PRUint32) atoi(cp + 8);
-        else
-            mIdleTimeout = gHttpHandler->IdleTimeout();
+        if (!mUsingSpdy) {
+            const char *cp = PL_strcasestr(val, "timeout=");
+            if (cp)
+                mIdleTimeout = (PRUint32) atoi(cp + 8);
+            else
+                mIdleTimeout = gHttpHandler->IdleTimeout();
+        }
+        else {
+            mIdleTimeout = gHttpHandler->SpdyTimeout();
+        }
         
         LOG(("Connection can be reused [this=%x idle-timeout=%u]\n", this, mIdleTimeout));
     }
 
+    if (!mProxyConnectStream)
+        HandleAlternateProtocol(responseHead);
+
     // if we're doing an SSL proxy connect, then we need to check whether or not
     // the connect was successful.  if so, then we have to reset the transaction
     // and step-up the socket connection to SSL. finally, we have to wake up the
     // socket write request.
     if (mProxyConnectStream) {
+        NS_ABORT_IF_FALSE(!mUsingSpdy,
+                          "SPDY NPN Complete while using proxy connect stream");
         mProxyConnectStream = 0;
         if (responseHead->Status() == 200) {
             LOG(("proxy CONNECT succeeded! ssl=%s\n",
                  mConnInfo->UsingSSL() ? "true" :"false"));
             *reset = true;
             nsresult rv;
             if (mConnInfo->UsingSSL()) {
                 rv = ProxyStartSSL();
@@ -501,16 +719,18 @@ nsHttpConnection::SetIsReusedAfter(PRUin
     mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
 }
 
 nsresult
 nsHttpConnection::TakeTransport(nsISocketTransport  **aTransport,
                                 nsIAsyncInputStream **aInputStream,
                                 nsIAsyncOutputStream **aOutputStream)
 {
+    if (mUsingSpdy)
+        return NS_ERROR_FAILURE;
     if (mTransaction && !mTransaction->IsDone())
         return NS_ERROR_IN_PROGRESS;
     if (!(mSocketTransport && mSocketIn && mSocketOut))
         return NS_ERROR_NOT_INITIALIZED;
 
     if (mInputOverflow)
         mSocketIn = mInputOverflow.forget();
 
@@ -548,31 +768,31 @@ nsHttpConnection::PushBack(const char *d
         return NS_ERROR_UNEXPECTED;
     }
     
     mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
     return NS_OK;
 }
 
 nsresult
-nsHttpConnection::ResumeSend()
+nsHttpConnection::ResumeSend(nsAHttpTransaction *)
 {
     LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketOut)
         return mSocketOut->AsyncWait(this, 0, 0, nsnull);
 
     NS_NOTREACHED("no socket output stream");
     return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
-nsHttpConnection::ResumeRecv()
+nsHttpConnection::ResumeRecv(nsAHttpTransaction *)
 {
     LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mSocketIn)
         return mSocketIn->AsyncWait(this, 0, 0, nsnull);
 
@@ -581,17 +801,18 @@ nsHttpConnection::ResumeRecv()
 }
 
 void
 nsHttpConnection::BeginIdleMonitoring()
 {
     LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ABORT_IF_FALSE(!mTransaction, "BeginIdleMonitoring() while active");
-    
+    NS_ABORT_IF_FALSE(!mUsingSpdy, "Idle monitoring of spdy not allowed");
+
     LOG(("Entering Idle Monitoring Mode [this=%p]", this));
     mIdleMonitoring = true;
     if (mSocketIn)
         mSocketIn->AsyncWait(this, 0, 0, nsnull);
 }
 
 void
 nsHttpConnection::EndIdleMonitoring()
@@ -623,16 +844,22 @@ nsHttpConnection::CloseTransaction(nsAHt
 
     if (mCurrentBytesRead > mMaxBytesRead)
         mMaxBytesRead = mCurrentBytesRead;
 
     // mask this error code because its not a real error.
     if (reason == NS_BASE_STREAM_CLOSED)
         reason = NS_OK;
 
+    if (mUsingSpdy) {
+        DontReuse();
+        mUsingSpdy = false;
+        mSpdySession = nsnull;
+    }
+
     mTransaction->Close(reason);
     mTransaction = nsnull;
 
     if (mCallbacks) {
         nsIInterfaceRequestor *cbs = nsnull;
         mCallbacks.swap(cbs);
         NS_ProxyRelease(mCallbackTarget, cbs);
     }
@@ -687,30 +914,51 @@ nsHttpConnection::OnSocketWritable()
 {
     LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n", this));
 
     nsresult rv;
     PRUint32 n;
     bool again = true;
 
     do {
+        mSocketOutCondition = NS_OK;
+
         // if we're doing an SSL proxy connect, then we need to bypass calling
         // into the transaction.
         //
         // NOTE: this code path can't be shared since the transaction doesn't
         // implement nsIInputStream.  doing so is not worth the added cost of
         // extra indirections during normal reading.
         //
         if (mProxyConnectStream) {
             LOG(("  writing CONNECT request stream\n"));
             rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
                                                       nsIOService::gDefaultSegmentSize,
                                                       &n);
         }
+        else if (!EnsureNPNComplete()) {
+            // When SPDY is disabled this branch is not executed because Activate()
+            // sets mNPNComplete to true in that case.
+
+            // We are ready to proceed with SSL but the handshake is not done.
+            // When using NPN to negotiate between HTTPS and SPDY, we need to
+            // see the results of the handshake to know what bytes to send, so
+            // we cannot proceed with the request headers.
+
+            rv = NS_OK;
+            mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
+            n = 0;
+        }
         else {
+            if (gHttpHandler->IsSpdyEnabled() && !mReportedSpdy) {
+                mReportedSpdy = true;
+                gHttpHandler->ConnMgr()->
+                    ReportSpdyConnection(this, mUsingSpdy);
+            }
+
             LOG(("  writing transaction request stream\n"));
             rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
         }
 
         LOG(("  ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
             rv, n, mSocketOutCondition));
 
         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
@@ -826,16 +1074,18 @@ nsHttpConnection::OnSocketReadable()
 nsresult
 nsHttpConnection::SetupProxyConnect()
 {
     const char *val;
 
     LOG(("nsHttpConnection::SetupProxyConnect [this=%x]\n", this));
 
     NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
+    NS_ABORT_IF_FALSE(!mUsingSpdy,
+                      "SPDY NPN Complete while using proxy connect stream");
 
     nsCAutoString buf;
     nsresult rv = nsHttpHandler::GenerateHostPort(
             nsDependentCString(mConnInfo->Host()), mConnInfo->Port(), buf);
     if (NS_FAILED(rv))
         return rv;
 
     // CONNECT host:port HTTP/1.1
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -42,16 +42,17 @@
 #include "nsHttp.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsAHttpConnection.h"
 #include "nsAHttpTransaction.h"
 #include "nsXPIDLString.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "prinrval.h"
+#include "SpdySession.h"
 
 #include "nsIStreamListener.h"
 #include "nsISocketTransport.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIEventTarget.h"
 
@@ -87,35 +88,36 @@ public:
     //                single transaction before it should no longer be kept 
     //                alive.  a value of 0xffff indicates no limit.
     nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime,
                   nsISocketTransport *, nsIAsyncInputStream *,
                   nsIAsyncOutputStream *, nsIInterfaceRequestor *,
                   nsIEventTarget *);
 
     // Activate causes the given transaction to be processed on this
-    // connection.  It fails if there is already an existing transaction.
-    nsresult Activate(nsAHttpTransaction *, PRUint8 caps);
+    // connection.  It fails if there is already an existing transaction unless
+    // a multiplexing protocol such as SPDY is being used
+    nsresult Activate(nsAHttpTransaction *, PRUint8 caps, PRInt32 pri);
 
     // Close the underlying socket transport.
     void Close(nsresult reason);
 
     //-------------------------------------------------------------------------
     // XXX document when these are ok to call
 
     bool     SupportsPipelining() { return mSupportsPipelining; }
-    bool     IsKeepAlive() { return mKeepAliveMask && mKeepAlive; }
+    bool     IsKeepAlive() { return mUsingSpdy ||
+                                    (mKeepAliveMask && mKeepAlive); }
     bool     CanReuse();   // can this connection be reused?
+    bool     CanDirectlyActivate();
 
     // Returns time in seconds for how long connection can be reused.
     PRUint32 TimeToLive();
 
-    void     DontReuse()   { mKeepAliveMask = false;
-                             mKeepAlive = false;
-                             mIdleTimeout = 0; }
+    void     DontReuse();
     void     DropTransport() { DontReuse(); mSocketTransport = 0; }
 
     bool     LastTransactionExpectedNoContent()
     {
         return mLastTransactionExpectedNoContent;
     }
 
     void     SetLastTransactionExpectedNoContent(bool val)
@@ -135,43 +137,57 @@ public:
                            nsIAsyncInputStream **,
                            nsIAsyncOutputStream **);
     void     GetSecurityInfo(nsISupports **);
     bool     IsPersistent() { return IsKeepAlive(); }
     bool     IsReused();
     void     SetIsReusedAfter(PRUint32 afterMilliseconds);
     void     SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
     nsresult PushBack(const char *data, PRUint32 length);
-    nsresult ResumeSend();
-    nsresult ResumeRecv();
+    nsresult ResumeSend(nsAHttpTransaction *caller);
+    nsresult ResumeRecv(nsAHttpTransaction *caller);
     PRInt64  MaxBytesRead() {return mMaxBytesRead;}
 
     static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
                                     PRUint32, PRUint32, PRUint32 *);
 
     // When a persistent connection is in the connection manager idle 
     // connection pool, the nsHttpConnection still reads errors and hangups
     // on the socket so that it can be proactively released if the server
     // initiates a termination. Only call on socket thread.
     void BeginIdleMonitoring();
     void EndIdleMonitoring();
 
+    bool UsingSpdy() { return mUsingSpdy; }
+
 private:
     // called to cause the underlying socket to start speaking SSL
     nsresult ProxyStartSSL();
 
     nsresult OnTransactionDone(nsresult reason);
     nsresult OnSocketWritable();
     nsresult OnSocketReadable();
 
     nsresult SetupProxyConnect();
 
     bool     IsAlive();
     bool     SupportsPipelining(nsHttpResponseHead *);
     
+    // Makes certain the SSL handshake is complete and NPN negotiation
+    // has had a chance to happen
+    bool     EnsureNPNComplete();
+    void     SetupNPN(PRUint8 caps);
+
+    // Inform the connection manager of any SPDY Alternate-Protocol
+    // redirections
+    void     HandleAlternateProtocol(nsHttpResponseHead *);
+
+    // Directly Add a transaction to an active connection for SPDY
+    nsresult AddTransaction(nsAHttpTransaction *, PRInt32);
+
 private:
     nsCOMPtr<nsISocketTransport>    mSocketTransport;
     nsCOMPtr<nsIAsyncInputStream>   mSocketIn;
     nsCOMPtr<nsIAsyncOutputStream>  mSocketOut;
 
     nsresult                        mSocketInCondition;
     nsresult                        mSocketOutCondition;
 
@@ -199,11 +215,19 @@ private:
 
     bool                            mKeepAlive;
     bool                            mKeepAliveMask;
     bool                            mSupportsPipelining;
     bool                            mIsReused;
     bool                            mCompletedProxyConnect;
     bool                            mLastTransactionExpectedNoContent;
     bool                            mIdleMonitoring;
+
+    // SPDY related
+    bool                            mNPNComplete;
+    bool                            mSetupNPNCalled;
+    bool                            mUsingSpdy;
+    nsRefPtr<mozilla::net::SpdySession> mSpdySession;
+    PRInt32                         mPriority;
+    bool                            mReportedSpdy;
 };
 
 #endif // nsHttpConnection_h__
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -119,17 +119,19 @@ public:
     const char   *Host() const           { return mHost.get(); }
     PRInt32       Port() const           { return mPort; }
     nsProxyInfo  *ProxyInfo()            { return mProxyInfo; }
     bool          UsingHttpProxy() const { return mUsingHttpProxy; }
     bool          UsingSSL() const       { return mUsingSSL; }
     PRInt32       DefaultPort() const    { return mUsingSSL ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT; }
     void          SetAnonymous(bool anon)         
                                          { mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
+    bool          GetAnonymous()         { return mHashKey.CharAt(2) == 'A'; }
     bool          ShouldForceConnectMethod();
+    const nsCString &GetHost() { return mHost; }
 
 private:
     nsrefcnt               mRef;
     nsCString              mHashKey;
     nsCString              mHost;
     PRInt32                mPort;
     nsCOMPtr<nsProxyInfo>  mProxyInfo;
     bool                   mUsingHttpProxy;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -43,16 +43,19 @@
 #include "nsNetCID.h"
 #include "nsCOMPtr.h"
 #include "nsNetUtil.h"
 
 #include "nsIServiceManager.h"
 
 #include "nsIObserverService.h"
 
+#include "nsISSLSocketControl.h"
+#include "prnetdb.h"
+
 using namespace mozilla;
 
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 
 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
 
 //-----------------------------------------------------------------------------
@@ -89,16 +92,17 @@ nsHttpConnectionMgr::nsHttpConnectionMgr
     , mMaxPersistConnsPerProxy(0)
     , mIsShuttingDown(false)
     , mNumActiveConns(0)
     , mNumIdleConns(0)
     , mTimeOfNextWakeUp(LL_MAXUINT)
 {
     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
     mCT.Init();
+    mAlternateProtocolHash.Init(16);
 }
 
 nsHttpConnectionMgr::~nsHttpConnectionMgr()
 {
     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
 }
 
 nsresult
@@ -135,16 +139,17 @@ nsHttpConnectionMgr::Init(PRUint16 maxCo
                           PRUint16 maxPersistConnsPerProxy,
                           PRUint16 maxRequestDelay,
                           PRUint16 maxPipelinedRequests)
 {
     LOG(("nsHttpConnectionMgr::Init\n"));
 
     {
         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+        mSpdyPreferredHash.Init();
 
         mMaxConns = maxConns;
         mMaxConnsPerHost = maxConnsPerHost;
         mMaxConnsPerProxy = maxConnsPerProxy;
         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
         mMaxRequestDelay = maxRequestDelay;
         mMaxPipelinedRequests = maxPipelinedRequests;
@@ -224,18 +229,23 @@ nsHttpConnectionMgr::PruneDeadConnection
         mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
         mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
     } else {
         NS_WARNING("failed to create: timer for pruning the dead connections!");
     }
 }
 
 void
-nsHttpConnectionMgr::StopPruneDeadConnectionsTimer()
+nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
 {
+    // Leave the timer in place if there are connections that potentially
+    // need management
+    if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
+        return;
+
     LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
 
     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
     mTimeOfNextWakeUp = LL_MAXUINT;
     if (mTimer) {
         mTimer->Cancel();
         mTimer = NULL;
     }
@@ -384,40 +394,311 @@ nsHttpConnectionMgr::ProcessPendingQ(nsH
 
     NS_ADDREF(ci);
     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
     if (NS_FAILED(rv))
         NS_RELEASE(ci);
     return rv;
 }
 
+// Given a nsHttpConnectionInfo find the connection entry object that
+// contains either the nshttpconnection or nshttptransaction parameter.
+// Normally this is done by the hashkey lookup of connectioninfo,
+// but if spdy coalescing is in play it might be found in a redirected
+// entry
+nsHttpConnectionMgr::nsConnectionEntry *
+nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
+                                           nsHttpConnection *conn,
+                                           nsHttpTransaction *trans)
+{
+    if (!ci)
+        return nsnull;
+
+    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    
+    // If there is no sign of coalescing (or it is disabled) then just
+    // return the primary hash lookup
+    if (!gHttpHandler->IsSpdyEnabled() || !gHttpHandler->CoalesceSpdy() ||
+        !ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
+        return ent;
+
+    // If there is no preferred coalescing entry for this host (or the
+    // preferred entry is the one that matched the mCT hash lookup) then
+    // there is only option
+    nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
+    if (!preferred || (preferred == ent))
+        return ent;
+
+    if (conn) {
+        // The connection could be either in preferred or ent. It is most
+        // likely the only active connection in preferred - so start with that.
+        if (preferred->mActiveConns.Contains(conn))
+            return preferred;
+        if (preferred->mIdleConns.Contains(conn))
+            return preferred;
+    }
+    
+    if (trans && preferred->mPendingQ.Contains(trans))
+        return preferred;
+    
+    // Neither conn nor trans found in preferred, use the default entry
+    return ent;
+}
+
 nsresult
 nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
 {
     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
          this, conn));
 
-    nsHttpConnectionInfo *ci = conn->ConnectionInfo();
-    if (!ci)
+    if (!conn->ConnectionInfo())
         return NS_ERROR_UNEXPECTED;
 
-    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
+                                                   conn, nsnull);
 
     if (!ent || !ent->mIdleConns.RemoveElement(conn))
         return NS_ERROR_UNEXPECTED;
 
     conn->Close(NS_ERROR_ABORT);
     NS_RELEASE(conn);
     mNumIdleConns--;
-    if (0 == mNumIdleConns)
-        StopPruneDeadConnectionsTimer();
+    ConditionallyStopPruneDeadConnectionsTimer();
     return NS_OK;
 }
 
+void
+nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
+                                          bool usingSpdy)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    
+    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
+                                                   conn, nsnull);
+
+    NS_ABORT_IF_FALSE(ent, "no connection entry");
+    if (!ent)
+        return;
+
+    ent->mTestedSpdy = true;
+
+    if (!usingSpdy) {
+        if (ent->mUsingSpdy)
+            conn->DontReuse();
+        return;
+    }
+    
+    ent->mUsingSpdy = true;
+
+    PRUint32 ttl = conn->TimeToLive();
+    PRUint64 timeOfExpire = NowInSeconds() + ttl;
+    if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
+        PruneDeadConnectionsAfter(ttl);
+
+    // Lookup preferred directly from the hash instead of using
+    // GetSpdyPreferred() because we want to avoid the cert compatibility
+    // check at this point because the cert is never part of the hash
+    // lookup. Filtering on that has to be done at the time of use
+    // rather than the time of registration (i.e. now).
+    nsConnectionEntry *preferred =
+        mSpdyPreferredHash.Get(ent->mCoalescingKey);
+
+    LOG(("ReportSpdyConnection %s %s ent=%p ispreferred=%d\n",
+         ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
+         ent, preferred));
+    
+    if (!preferred) {
+        ent->mSpdyPreferred = true;
+        SetSpdyPreferred(ent);
+        preferred = ent;
+    }
+    else if (preferred != ent) {
+        // A different hostname is the preferred spdy host for this
+        // IP address.
+        ent->mUsingSpdy = true;
+        conn->DontReuse();
+    }
+
+    ProcessSpdyPendingQ();
+}
+
+bool
+nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
+{
+    // The Alternate Protocol hash is protected under the monitor because
+    // it is read from both the main and the network thread.
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    return mAlternateProtocolHash.Contains(hostPortKey);
+}
+
+void
+nsHttpConnectionMgr::ReportSpdyAlternateProtocol(nsHttpConnection *conn)
+{
+    // Check network.http.spdy.use-alternate-protocol pref
+    if (!gHttpHandler->UseAlternateProtocol())
+        return;
+
+    // For now lets not bypass proxies due to the alternate-protocol header
+    if (conn->ConnectionInfo()->UsingHttpProxy())
+        return;
+
+    nsCString hostPortKey(conn->ConnectionInfo()->Host());
+    if (conn->ConnectionInfo()->Port() != 80) {
+        hostPortKey.Append(NS_LITERAL_CSTRING(":"));
+        hostPortKey.AppendInt(conn->ConnectionInfo()->Port());
+    }
+
+    // The Alternate Protocol hash is protected under the monitor because
+    // it is read from both the main and the network thread.
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // Check to see if this is already present
+    if (mAlternateProtocolHash.Contains(hostPortKey))
+        return;
+    
+    if (mAlternateProtocolHash.mHashTable.entryCount > 2000)
+        PL_DHashTableEnumerate(&mAlternateProtocolHash.mHashTable,
+                               &nsHttpConnectionMgr::TrimAlternateProtocolHash,
+                               this);
+    
+    mAlternateProtocolHash.Put(hostPortKey);
+}
+
+void
+nsHttpConnectionMgr::RemoveSpdyAlternateProtocol(nsACString &hostPortKey)
+{
+    // The Alternate Protocol hash is protected under the monitor because
+    // it is read from both the main and the network thread.
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    return mAlternateProtocolHash.Remove(hostPortKey);
+}
+
+PLDHashOperator
+nsHttpConnectionMgr::TrimAlternateProtocolHash(PLDHashTable *table,
+                                               PLDHashEntryHdr *hdr,
+                                               PRUint32 number,
+                                               void *closure)
+{
+    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
+    
+    if (self->mAlternateProtocolHash.mHashTable.entryCount > 2000)
+        return PL_DHASH_REMOVE;
+    return PL_DHASH_STOP;
+}
+
+nsHttpConnectionMgr::nsConnectionEntry *
+nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
+{
+    if (!gHttpHandler->IsSpdyEnabled() ||
+        !gHttpHandler->CoalesceSpdy() ||
+        aOriginalEntry->mCoalescingKey.IsEmpty())
+        return nsnull;
+
+    nsConnectionEntry *preferred =
+        mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
+
+    // if there is no redirection no cert validation is required
+    if (preferred == aOriginalEntry)
+        return aOriginalEntry;
+
+    // if there is no preferred host or it is no longer using spdy
+    // then skip pooling
+    if (!preferred || !preferred->mUsingSpdy)
+        return nsnull;                         
+
+    // if there is not an active spdy session in this entry then
+    // we cannot pool because the cert upon activation may not
+    // be the same as the old one. Active sessions are prohibited
+    // from changing certs.
+
+    nsHttpConnection *activeSpdy = nsnull;
+
+    for (PRUint32 index = 0; index < preferred->mActiveConns.Length(); ++index) {
+        if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
+            activeSpdy = preferred->mActiveConns[index];
+            break;
+        }
+    }
+
+    if (!activeSpdy) {
+        // remove the preferred status of this entry if it cannot be
+        // used for pooling.
+        preferred->mSpdyPreferred = false;
+        RemoveSpdyPreferred(preferred->mCoalescingKey);
+        LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
+             "preferred host mapping %s to %s removed due to inactivity.\n",
+             aOriginalEntry->mConnInfo->Host(),
+             preferred->mConnInfo->Host()));
+
+        return nsnull;
+    }
+
+    // Check that the server cert supports redirection
+    nsresult rv;
+    bool isJoined = false;
+
+    nsCOMPtr<nsISupports> securityInfo;
+    nsCOMPtr<nsISSLSocketControl> sslSocketControl;
+    nsCAutoString negotiatedNPN;
+    
+    activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
+    if (!securityInfo)
+        return nsnull;
+
+    sslSocketControl = do_QueryInterface(securityInfo, &rv);
+    if (NS_FAILED(rv))
+        return nsnull;
+
+    rv = sslSocketControl->JoinConnection(NS_LITERAL_CSTRING("spdy/2"),
+                                          aOriginalEntry->mConnInfo->GetHost(),
+                                          aOriginalEntry->mConnInfo->Port(),
+                                          &isJoined);
+
+    if (NS_FAILED(rv) || !isJoined) {
+        LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
+             "Host %s cannot be confirmed to be joined "
+             "with %s connections",
+             preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+        return nsnull;
+    }
+
+    // IP pooling confirmed
+    LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
+         "Host %s has cert valid for %s connections",
+         preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
+    return preferred;
+}
+
+void
+nsHttpConnectionMgr::SetSpdyPreferred(nsConnectionEntry *ent)
+{
+    if (!gHttpHandler->CoalesceSpdy())
+        return;
+
+    if (ent->mCoalescingKey.IsEmpty())
+        return;
+    
+    mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
+}
+
+void
+nsHttpConnectionMgr::RemoveSpdyPreferred(nsACString &aHashKey)
+{
+    if (!gHttpHandler->CoalesceSpdy())
+        return;
+
+    if (aHashKey.IsEmpty())
+        return;
+    
+    mSpdyPreferredHash.Remove(aHashKey);
+}
+
 //-----------------------------------------------------------------------------
 // enumeration callbacks
 
 PLDHashOperator
 nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
                                              nsAutoPtr<nsConnectionEntry> &ent,
                                              void *closure)
 {
@@ -444,79 +725,101 @@ nsHttpConnectionMgr::PurgeExcessIdleConn
             // There are no idle conns left in this connection entry
             return PL_DHASH_NEXT;
         }
         nsHttpConnection *conn = ent->mIdleConns[0];
         ent->mIdleConns.RemoveElementAt(0);
         conn->Close(NS_ERROR_ABORT);
         NS_RELEASE(conn);
         self->mNumIdleConns--;
-        if (0 == self->mNumIdleConns)
-            self->StopPruneDeadConnectionsTimer();
+        self->ConditionallyStopPruneDeadConnectionsTimer();
     }
     return PL_DHASH_STOP;
 }
 
 PLDHashOperator
 nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
                                             nsAutoPtr<nsConnectionEntry> &ent,
                                             void *closure)
 {
     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
 
     LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
 
     // Find out how long it will take for next idle connection to not be reusable
     // anymore.
+    bool liveConnections = false;
     PRUint32 timeToNextExpire = PR_UINT32_MAX;
     PRInt32 count = ent->mIdleConns.Length();
     if (count > 0) {
         for (PRInt32 i=count-1; i>=0; --i) {
             nsHttpConnection *conn = ent->mIdleConns[i];
             if (!conn->CanReuse()) {
                 ent->mIdleConns.RemoveElementAt(i);
                 conn->Close(NS_ERROR_ABORT);
                 NS_RELEASE(conn);
                 self->mNumIdleConns--;
             } else {
                 timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
+                liveConnections = true;
             }
         }
     }
 
+    if (ent->mUsingSpdy) {
+        for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
+            nsHttpConnection *conn = ent->mActiveConns[index];
+            if (conn->UsingSpdy()) {
+                if (!conn->CanReuse()) {
+                    // marking it dont reuse will create an active tear down if
+                    // the spdy session is idle.
+                    conn->DontReuse();
+                }
+                else {
+                    timeToNextExpire = NS_MIN(timeToNextExpire,
+                                              conn->TimeToLive());
+                    liveConnections = true;
+                }
+            }
+        }
+    }
+    
     // If time to next expire found is shorter than time to next wake-up, we need to
     // change the time for next wake-up.
-    PRUint32 now = NowInSeconds();
-    if (0 < ent->mIdleConns.Length()) {
+    if (liveConnections) {
+        PRUint32 now = NowInSeconds();
         PRUint64 timeOfNextExpire = now + timeToNextExpire;
         // If pruning of dead connections is not already scheduled to happen
         // or time found for next connection to expire is is before
         // mTimeOfNextWakeUp, we need to schedule the pruning to happen
         // after timeToNextExpire.
         if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
             self->PruneDeadConnectionsAfter(timeToNextExpire);
         }
-    } else if (0 == self->mNumIdleConns) {
-        self->StopPruneDeadConnectionsTimer();
+    } else {
+        self->ConditionallyStopPruneDeadConnectionsTimer();
     }
 #ifdef DEBUG
     count = ent->mActiveConns.Length();
     if (count > 0) {
         for (PRInt32 i=count-1; i>=0; --i) {
             nsHttpConnection *conn = ent->mActiveConns[i];
             LOG(("    active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
         }
     }
 #endif
 
     // if this entry is empty, then we can remove it.
     if (ent->mIdleConns.Length()   == 0 &&
         ent->mActiveConns.Length() == 0 &&
         ent->mHalfOpens.Length()   == 0 &&
-        ent->mPendingQ.Length()    == 0) {
+        ent->mPendingQ.Length()    == 0 &&
+        ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
+         !gHttpHandler->IsSpdyEnabled() ||
+         self->mCT.Count() > 300)) {
         LOG(("    removing empty connection entry\n"));
         return PL_DHASH_REMOVE;
     }
 
     // else, use this opportunity to compact our arrays...
     ent->mIdleConns.Compact();
     ent->mActiveConns.Compact();
     ent->mPendingQ.Compact();
@@ -552,18 +855,17 @@ nsHttpConnectionMgr::ShutdownPassCB(cons
         ent->mIdleConns.RemoveElementAt(0);
         self->mNumIdleConns--;
 
         conn->Close(NS_ERROR_ABORT);
         NS_RELEASE(conn);
     }
     // If all idle connections are removed,
     // we can stop pruning dead connections.
-    if (0 == self->mNumIdleConns)
-        self->StopPruneDeadConnectionsTimer();
+    self->ConditionallyStopPruneDeadConnectionsTimer();
 
     // close all pending transactions
     while (ent->mPendingQ.Length()) {
         trans = ent->mPendingQ[0];
 
         ent->mPendingQ.RemoveElementAt(0);
 
         trans->Close(NS_ERROR_ABORT);
@@ -577,25 +879,29 @@ nsHttpConnectionMgr::ShutdownPassCB(cons
     return PL_DHASH_REMOVE;
 }
 
 //-----------------------------------------------------------------------------
 
 bool
 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
 {
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
         ent->mConnInfo->HashKey().get()));
 
-    PRInt32 i, count = ent->mPendingQ.Length();
+    if (gHttpHandler->IsSpdyEnabled())
+        ProcessSpdyPendingQ(ent);
+
+    PRUint32 i, count = ent->mPendingQ.Length();
     if (count > 0) {
         LOG(("  pending-count=%u\n", count));
         nsHttpTransaction *trans = nsnull;
         nsHttpConnection *conn = nsnull;
-        for (i=0; i<count; ++i) {
+        for (i = 0; i < count; ++i) {
             trans = ent->mPendingQ[i];
 
             // When this transaction has already established a half-open
             // connection, we want to prevent any duplicate half-open
             // connections from being established and bound to this
             // transaction. Allow only use of an idle persistent connection
             // (if found) for transactions referred by a half-open connection.
             bool alreadyHalfOpen = false;
@@ -604,16 +910,23 @@ nsHttpConnectionMgr::ProcessPendingQForE
                     alreadyHalfOpen = true;
                     break;
                 }
             }
 
             GetConnection(ent, trans, alreadyHalfOpen, &conn);
             if (conn)
                 break;
+
+            // Check to see if a pending transaction was dispatched with the
+            // coalesce logic
+            if (count != ent->mPendingQ.Length()) {
+                count = ent->mPendingQ.Length();
+                i = 0;
+            }
         }
         if (conn) {
             LOG(("  dispatching pending transaction...\n"));
 
             // remove pending transaction
             ent->mPendingQ.RemoveElementAt(i);
 
             nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
@@ -731,26 +1044,37 @@ void
 nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
                                    nsHttpTransaction *trans,
                                    bool onlyReusedConnection,
                                    nsHttpConnection **result)
 {
     LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
         ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
 
-    // First, see if an idle persistent connection may be reused instead of
+    // First, see if an existing connection can be used - either an idle
+    // persistent connection or an active spdy session may be reused instead of
     // establishing a new socket. We do not need to check the connection limits
     // yet as they govern the maximum number of open connections and reusing
     // an old connection never increases that.
 
     *result = nsnull;
 
     nsHttpConnection *conn = nsnull;
+    bool addConnToActiveList = true;
 
     if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
+
+        // if willing to use spdy look for an active spdy connections
+        // before considering idle http ones.
+        if (gHttpHandler->IsSpdyEnabled()) {
+            conn = GetSpdyPreferredConn(ent);
+            if (conn)
+                addConnToActiveList = false;
+        }
+        
         // search the idle connection list. Each element in the list
         // has a reference, so if we remove it from the list into a local
         // ptr, that ptr now owns the reference
         while (!conn && (ent->mIdleConns.Length() > 0)) {
             conn = ent->mIdleConns[0];
             // we check if the connection can be reused before even checking if
             // it is a "matching" connection.
             if (!conn->CanReuse()) {
@@ -763,28 +1087,40 @@ nsHttpConnectionMgr::GetConnection(nsCon
                 conn->EndIdleMonitoring();
             }
 
             ent->mIdleConns.RemoveElementAt(0);
             mNumIdleConns--;
 
             // If there are no idle connections left at all, we need to make
             // sure that we are not pruning dead connections anymore.
-            if (0 == mNumIdleConns)
-                StopPruneDeadConnectionsTimer();
+            ConditionallyStopPruneDeadConnectionsTimer();
         }
     }
 
     if (!conn) {
 
         // If the onlyReusedConnection parameter is TRUE, then GetConnection()
         // does not create new transports under any circumstances.
         if (onlyReusedConnection)
             return;
         
+        if (gHttpHandler->IsSpdyEnabled() &&
+            ent->mConnInfo->UsingSSL() &&
+            !ent->mConnInfo->UsingHttpProxy())
+        {
+            // If this is a possible Spdy connection we need to limit the number
+            // of connections outstanding to 1 while we wait for the spdy/https
+            // ReportSpdyConnection()
+    
+            if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
+                (ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
+                return;
+        }
+        
         // Check if we need to purge an idle connection. Note that we may have
         // removed one above; if so, this will be a no-op. We do this before
         // checking the active connection limit to catch the case where we do
         // have an idle connection, but the purge timer hasn't fired yet.
         // XXX this just purges a random idle connection.  we should instead
         // enumerate the entire hash table to find the eldest idle connection.
         if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
             mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
@@ -794,27 +1130,34 @@ nsHttpConnectionMgr::GetConnection(nsCon
         // host or proxy. If we have, we're done.
         if (AtActiveConnectionLimit(ent, trans->Caps())) {
             LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
                  "at active connection limit - will queue\n",
                  ent->mConnInfo->HashKey().get()));
             return;
         }
 
+        LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
+             "%s %s ent=%p spdy=%d",
+             ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
+             ent, ent->mUsingSpdy));
+        
         nsresult rv = CreateTransport(ent, trans);
         if (NS_FAILED(rv))
             trans->Close(rv);
         return;
     }
 
-    // hold an owning ref to this connection
-    ent->mActiveConns.AppendElement(conn);
-    mNumActiveConns++;
+    if (addConnToActiveList) {
+        // hold an owning ref to this connection
+        ent->mActiveConns.AppendElement(conn);
+        mNumActiveConns++;
+    }
+    
     NS_ADDREF(conn);
-
     *result = conn;
 }
 
 void
 nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
                                    nsConnectionEntry *ent)
 {
     NS_ADDREF(conn);
@@ -845,39 +1188,54 @@ nsHttpConnectionMgr::CreateTransport(nsC
     NS_ENSURE_SUCCESS(rv, rv);
 
     ent->mHalfOpens.AppendElement(sock);
     return NS_OK;
 }
 
 nsresult
 nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
-                                         nsAHttpTransaction *trans,
+                                         nsHttpTransaction *aTrans,
                                          PRUint8 caps,
                                          nsHttpConnection *conn)
 {
     LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
-        ent->mConnInfo->HashKey().get(), trans, caps, conn));
+        ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
+    nsresult rv;
+    
+    PRInt32 priority = aTrans->Priority();
+
+    if (conn->UsingSpdy()) {
+        LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
+             "Connection host = %s\n",
+             aTrans->ConnectionInfo()->Host(),
+             conn->ConnectionInfo()->Host()));
+        rv = conn->Activate(aTrans, caps, priority);
+        NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
+        return rv;
+    }
 
     nsConnectionHandle *handle = new nsConnectionHandle(conn);
     if (!handle)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(handle);
 
     nsHttpPipeline *pipeline = nsnull;
+    nsAHttpTransaction *trans = aTrans;
+
     if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
         LOG(("  looking to build pipeline...\n"));
         if (BuildPipeline(ent, trans, &pipeline))
             trans = pipeline;
     }
 
     // give the transaction the indirect reference to the connection.
     trans->SetConnection(handle);
 
-    nsresult rv = conn->Activate(trans, caps);
+    rv = conn->Activate(trans, caps, priority);
 
     if (NS_FAILED(rv)) {
         LOG(("  conn->Activate failed [rv=%x]\n", rv));
         ent->mActiveConns.RemoveElement(conn);
         mNumActiveConns--;
         // sever back references to connection, and do so without triggering
         // a call to ReclaimConnection ;-)
         trans->SetConnection(nsnull);
@@ -962,16 +1320,27 @@ nsHttpConnectionMgr::ProcessNewTransacti
         if (!clone)
             return NS_ERROR_OUT_OF_MEMORY;
         ent = new nsConnectionEntry(clone);
         if (!ent)
             return NS_ERROR_OUT_OF_MEMORY;
         mCT.Put(ci->HashKey(), ent);
     }
 
+    // SPDY coalescing of hostnames means we might redirect from this
+    // connection entry onto the preferred one.
+    nsConnectionEntry *preferredEntry = GetSpdyPreferred(ent);
+    if (preferredEntry && (preferredEntry != ent)) {
+        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
+             "redirected via coalescing from %s to %s\n", trans,
+             ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
+
+        ent = preferredEntry;
+    }
+
     // If we are doing a force reload then close out any existing conns
     // to this host so that changes in DNS, LBs, etc.. are reflected
     if (caps & NS_HTTP_CLEAR_KEEPALIVES)
         ClosePersistentConnections(ent);
 
     // Check if the transaction already has a sticky reference to a connection.
     // If so, then we can just use it directly by transferring its reference
     // to the new connection var instead of calling GetConnection() to search
@@ -1001,16 +1370,92 @@ nsHttpConnectionMgr::ProcessNewTransacti
     else {
         rv = DispatchTransaction(ent, trans, caps, conn);
         NS_RELEASE(conn);
     }
 
     return rv;
 }
 
+// This function tries to dispatch the pending spdy transactions on
+// the connection entry sent in as an argument. It will do so on the
+// active spdy connection either in that same entry or in the
+// redirected 'preferred' entry for the same coalescing hash key if
+// coalescing is enabled.
+
+void
+nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
+{
+    nsHttpConnection *conn = GetSpdyPreferredConn(ent);
+    if (!conn)
+        return;
+
+    for (PRInt32 index = ent->mPendingQ.Length() - 1;
+         index >= 0 && conn->CanDirectlyActivate();
+         --index) {
+        nsHttpTransaction *trans = ent->mPendingQ[index];
+
+        if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
+            trans->Caps() & NS_HTTP_DISALLOW_SPDY)
+            continue;
+ 
+        ent->mPendingQ.RemoveElementAt(index);
+
+        nsresult rv2 = DispatchTransaction(ent, trans, trans->Caps(), conn);
+        NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv2), "Dispatch SPDY Transaction");
+        NS_RELEASE(trans);
+    }
+}
+
+PLDHashOperator
+nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
+                                           nsAutoPtr<nsConnectionEntry> &ent,
+                                           void *closure)
+{
+    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
+    self->ProcessSpdyPendingQ(ent);
+    return PL_DHASH_NEXT;
+}
+
+void
+nsHttpConnectionMgr::ProcessSpdyPendingQ()
+{
+    mCT.Enumerate(ProcessSpdyPendingQCB, this);
+}
+
+nsHttpConnection *
+nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
+{
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    NS_ABORT_IF_FALSE(ent, "no connection entry");
+
+    nsConnectionEntry *preferred = GetSpdyPreferred(ent);
+
+    // this entry is spdy-enabled if it is involved in a redirect
+    if (preferred)
+        ent->mUsingSpdy = true;
+    else
+        preferred = ent;
+    
+    nsHttpConnection *conn = nsnull;
+    
+    if (preferred->mUsingSpdy) {
+        for (PRUint32 index = 0;
+             index < preferred->mActiveConns.Length();
+             ++index) {
+            if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
+                conn = preferred->mActiveConns[index];
+                break;
+            }
+        }
+    }
+    
+    return conn;
+}
+
 //-----------------------------------------------------------------------------
 
 void
 nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
 {
     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
 
     mCT.Enumerate(ShutdownPassCB, this);
@@ -1036,18 +1481,19 @@ nsHttpConnectionMgr::OnMsgNewTransaction
 void
 nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
 {
     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
 
     nsHttpTransaction *trans = (nsHttpTransaction *) param;
     trans->SetPriority(priority);
 
-    nsHttpConnectionInfo *ci = trans->ConnectionInfo();
-    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
+                                                   nsnull, trans);
+
     if (ent) {
         PRInt32 index = ent->mPendingQ.IndexOf(trans);
         if (index >= 0) {
             ent->mPendingQ.RemoveElementAt(index);
             InsertTransactionSorted(ent->mPendingQ, trans);
         }
     }
 
@@ -1064,18 +1510,19 @@ nsHttpConnectionMgr::OnMsgCancelTransact
     // if the transaction owns a connection and the transaction is not done,
     // then ask the connection to close the transaction.  otherwise, close the
     // transaction directly (removing it from the pending queue first).
     //
     nsAHttpConnection *conn = trans->Connection();
     if (conn && !trans->IsDone())
         conn->CloseTransaction(trans, reason);
     else {
-        nsHttpConnectionInfo *ci = trans->ConnectionInfo();
-        nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+        nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
+                                                       nsnull, trans);
+
         if (ent) {
             PRInt32 index = ent->mPendingQ.IndexOf(trans);
             if (index >= 0) {
                 ent->mPendingQ.RemoveElementAt(index);
                 nsHttpTransaction *temp = trans;
                 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
             }
         }
@@ -1104,17 +1551,20 @@ nsHttpConnectionMgr::OnMsgProcessPending
 
 void
 nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
 {
     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
 
     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
     mTimeOfNextWakeUp = LL_MAXUINT;
-    if (mNumIdleConns > 0) 
+
+    // check canreuse() for all idle connections plus any active connections on
+    // connection entries that are using spdy.
+    if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
         mCT.Enumerate(PruneDeadConnectionsCB, this);
 }
 
 void
 nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
 {
     LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
 
@@ -1129,27 +1579,36 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
     nsHttpConnection *conn = (nsHttpConnection *) param;
 
     // 
     // 1) remove the connection from the active list
     // 2) if keep-alive, add connection to idle list
     // 3) post event to process the pending transaction queue
     //
 
-    nsHttpConnectionInfo *ci = conn->ConnectionInfo();
-    NS_ADDREF(ci);
+    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
+                                                   conn, nsnull);
+    nsHttpConnectionInfo *ci = nsnull;
 
-    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
+    if (!ent) {
+        // this should never happen
+        NS_ASSERTION(ent, "no connection entry");
+        NS_ADDREF(ci = conn->ConnectionInfo());
+    }
+    else {
+        NS_ADDREF(ci = ent->mConnInfo);
 
-    NS_ASSERTION(ent, "no connection entry");
-    if (ent) {
         // If the connection is in the active list, remove that entry
         // and the reference held by the mActiveConns list.
         // This is never the final reference on conn as the event context
         // is also holding one that is released at the end of this function.
+
+        if (ent->mUsingSpdy)
+            conn->DontReuse();
+
         if (ent->mActiveConns.RemoveElement(conn)) {
             nsHttpConnection *temp = conn;
             NS_RELEASE(temp);
             mNumActiveConns--;
         }
 
         if (conn->CanReuse()) {
             LOG(("  adding connection to idle list\n"));
@@ -1168,17 +1627,17 @@ nsHttpConnectionMgr::OnMsgReclaimConnect
             }
 
             NS_ADDREF(conn);
             ent->mIdleConns.InsertElementAt(idx, conn);
             mNumIdleConns++;
             conn->BeginIdleMonitoring();
 
             // If the added connection was first idle connection or has shortest
-            // time to live among the idle connections, pruning dead
+            // time to live among the watched connections, pruning dead
             // connections needs to be done when it can't be reused anymore.
             PRUint32 timeToLive = conn->TimeToLive();
             if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
                 PruneDeadConnectionsAfter(timeToLive);
         }
         else {
             LOG(("  connection cannot be reused; closing connection\n"));
             // make sure the connection is closed and release our reference.
@@ -1218,16 +1677,25 @@ nsHttpConnectionMgr::OnMsgUpdateParam(PR
     case MAX_PIPELINED_REQUESTS:
         mMaxPipelinedRequests = value;
         break;
     default:
         NS_NOTREACHED("unexpected parameter name");
     }
 }
 
+// nsHttpConnectionMgr::nsConnectionEntry
+nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
+{
+    if (mSpdyPreferred)
+        gHttpHandler->ConnMgr()->RemoveSpdyPreferred(mCoalescingKey);
+
+    NS_RELEASE(mConnInfo);
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpConnectionMgr::nsConnectionHandle
 
 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
 {
     if (mConn) {
         gHttpHandler->ReclaimConnection(mConn);
         NS_RELEASE(mConn);
@@ -1241,25 +1709,25 @@ nsHttpConnectionMgr::nsConnectionHandle:
                                                             nsHttpRequestHead *req,
                                                             nsHttpResponseHead *resp,
                                                             bool *reset)
 {
     return mConn->OnHeadersAvailable(trans, req, resp, reset);
 }
 
 nsresult
-nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
+nsHttpConnectionMgr::nsConnectionHandle::ResumeSend(nsAHttpTransaction *caller)
 {
-    return mConn->ResumeSend();
+    return mConn->ResumeSend(caller);
 }
 
 nsresult
-nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
+nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv(nsAHttpTransaction *caller)
 {
-    return mConn->ResumeRecv();
+    return mConn->ResumeRecv(caller);
 }
 
 void
 nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     mConn->CloseTransaction(trans, reason);
 }
 
@@ -1411,20 +1879,24 @@ nsHalfOpenSocket::SetupStreams(nsISocket
         gHttpHandler->ConnMgr()->StartedConnect();
 
     return rv;
 }
 
 nsresult
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
 {
-    nsresult rv = SetupStreams(getter_AddRefs(mSocketTransport),
-                               getter_AddRefs(mStreamIn),
-                               getter_AddRefs(mStreamOut),
-                               false);
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    nsresult rv;
+
+    rv = SetupStreams(getter_AddRefs(mSocketTransport),
+                      getter_AddRefs(mStreamIn),
+                      getter_AddRefs(mStreamOut),
+                      false);
     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
          this, mEnt->mConnInfo->Host(), rv));
     if (NS_FAILED(rv)) {
         if (mStreamOut)
             mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
         mStreamOut = nsnull;
         mStreamIn = nsnull;
         mSocketTransport = nsnull;
@@ -1451,16 +1923,17 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
     return rv;
 }
 
 void
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
 {
     PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
     NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
+    
     if (timeout) {
         // Setup the timer that will establish a backup socket
         // if we do not get a writable event on the main one.
         // We do this because a lost SYN takes a very long time
         // to repair at the TCP level.
         //
         // Failure to setup the timer is something we can live with,
         // so don't return an error in that case.
@@ -1616,16 +2089,52 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
 
 // method for nsITransportEventSink
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
                                                          nsresult status,
                                                          PRUint64 progress,
                                                          PRUint64 progressMax)
 {
+    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+
+    // if we are doing spdy coalescing and haven't recorded the ip address
+    // for this entry before then make the hash key if our dns lookup
+    // just completed
+
+    if (gHttpHandler->IsSpdyEnabled() &&
+        gHttpHandler->CoalesceSpdy() &&
+        mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
+        !mEnt->mConnInfo->UsingHttpProxy() &&
+        mEnt->mCoalescingKey.IsEmpty() &&
+        status == nsISocketTransport::STATUS_CONNECTED_TO) {
+
+        PRNetAddr addr;
+        nsresult rv = mSocketTransport->GetPeerAddr(&addr);
+        if (NS_SUCCEEDED(rv)) {
+            mEnt->mCoalescingKey.SetCapacity(72);
+            PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64);
+            mEnt->mCoalescingKey.SetLength(
+                strlen(mEnt->mCoalescingKey.BeginReading()));
+
+            if (mEnt->mConnInfo->GetAnonymous())
+                mEnt->mCoalescingKey.AppendLiteral("~A:");
+            else
+                mEnt->mCoalescingKey.AppendLiteral("~.:");
+            mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
+
+            LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
+                 "STATUS_CONNECTED_TO Established New Coalescing Key for host "
+                 "%s [%s]", mEnt->mConnInfo->Host(),
+                 mEnt->mCoalescingKey.get()));
+
+            gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
+        }
+    }
+
     if (mTransaction)
         mTransaction->OnTransportStatus(trans, status, progress);
 
     if (trans != mSocketTransport)
         return NS_OK;
 
     switch (status) {
     case nsISocketTransport::STATUS_CONNECTING_TO:
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -40,22 +40,25 @@
 #define nsHttpConnectionMgr_h__
 
 #include "nsHttpConnectionInfo.h"
 #include "nsHttpConnection.h"
 #include "nsHttpTransaction.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsISocketTransportService.h"
+#include "nsHashSets.h"
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
+#include "nsIX509Cert3.h"
 
 class nsHttpPipeline;
 
 //-----------------------------------------------------------------------------
 
 class nsHttpConnectionMgr : public nsIObserver
 {
 public:
@@ -91,18 +94,19 @@ public:
     //-------------------------------------------------------------------------
     // NOTE: functions below may be called on any thread.
     //-------------------------------------------------------------------------
 
     // Schedules next pruning of dead connection to happen after
     // given time.
     void PruneDeadConnectionsAfter(PRUint32 time);
 
-    // Stops timer scheduled for next pruning of dead connections.
-    void StopPruneDeadConnectionsTimer();
+    // Stops timer scheduled for next pruning of dead connections if
+    // there are no more idle connections or active spdy ones
+    void ConditionallyStopPruneDeadConnectionsTimer();
 
     // adds a transaction to the list of managed transactions.
     nsresult AddTransaction(nsHttpTransaction *, PRInt32 priority);
 
     // called to reschedule the given transaction.  it must already have been
     // added to the connection manager via AddTransaction.
     nsresult RescheduleTransaction(nsHttpTransaction *, PRInt32 priority);
 
@@ -125,16 +129,21 @@ public:
     // connection can be reused then it will be added to the idle list, else
     // it will be closed.
     nsresult ReclaimConnection(nsHttpConnection *conn);
 
     // called to update a parameter after the connection manager has already
     // been initialized.
     nsresult UpdateParam(nsParamName name, PRUint16 value);
 
+    // Lookup/Cancel HTTP->SPDY redirections
+    bool GetSpdyAlternateProtocol(nsACString &key);
+    void ReportSpdyAlternateProtocol(nsHttpConnection *);
+    void RemoveSpdyAlternateProtocol(nsACString &key);
+
     //-------------------------------------------------------------------------
     // NOTE: functions below may be called only on the socket thread.
     //-------------------------------------------------------------------------
 
     // removes the next transaction for the specified connection from the
     // pending transaction queue.
     void AddTransactionToPipeline(nsHttpPipeline *);
 
@@ -142,40 +151,67 @@ public:
     // preference to the specified connection.
     nsresult ProcessPendingQ(nsHttpConnectionInfo *);
 
     // This is used to force an idle connection to be closed and removed from
     // the idle connection list. It is called when the idle connection detects
     // that the network peer has closed the transport.
     nsresult CloseIdleConnection(nsHttpConnection *);
 
+    // The connection manager needs to know when a normal HTTP connection has been
+    // upgraded to SPDY because the dispatch and idle semantics are a little
+    // bit different.
+    void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
+
 private:
     virtual ~nsHttpConnectionMgr();
     class nsHalfOpenSocket;
     
     // nsConnectionEntry
     //
     // mCT maps connection info hash key to nsConnectionEntry object, which
     // contains list of active and idle connections as well as the list of
     // pending transactions.
     //
     struct nsConnectionEntry
     {
         nsConnectionEntry(nsHttpConnectionInfo *ci)
-            : mConnInfo(ci)
+          : mConnInfo(ci),
+            mUsingSpdy(false),
+            mTestedSpdy(false),
+            mSpdyPreferred(false)
         {
             NS_ADDREF(mConnInfo);
         }
-       ~nsConnectionEntry() { NS_RELEASE(mConnInfo); }
+        ~nsConnectionEntry();
 
         nsHttpConnectionInfo        *mConnInfo;
         nsTArray<nsHttpTransaction*> mPendingQ;    // pending transaction queue
         nsTArray<nsHttpConnection*>  mActiveConns; // active connections
         nsTArray<nsHttpConnection*>  mIdleConns;   // idle persistent connections
         nsTArray<nsHalfOpenSocket*>  mHalfOpens;
+
+        // Spdy sometimes resolves the address in the socket manager in order
+        // to re-coalesce sharded HTTP hosts. The dotted decimal address is
+        // combined with the Anonymous flag from the connection information
+        // to build the hash key for hosts in the same ip pool.
+        //
+        // When a set of hosts are coalesced together one of them is marked
+        // mSpdyPreferred. The mapping is maintained in the connection mananger
+        // mSpdyPreferred hash.
+        //
+        nsCString mCoalescingKey;
+
+        // To have the UsingSpdy flag means some host with the same hash information
+        // has done NPN=spdy/2 at some point. It does not mean every connection
+        // is currently using spdy.
+        bool mUsingSpdy;
+
+        bool mTestedSpdy;
+        bool mSpdyPreferred;
     };
 
     // nsConnectionHandle
     //
     // thin wrapper around a real connection, used to keep track of references
     // to the connection to determine when the connection may be reused.  the
     // transaction (or pipeline) owns a reference to this handle.  this extra
     // layer of indirection greatly simplifies consumer code, avoiding the
@@ -268,27 +304,43 @@ private:
     static PLDHashOperator PruneDeadConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator ShutdownPassCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator PurgeExcessIdleConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     static PLDHashOperator ClosePersistentConnectionsCB(const nsACString &, nsAutoPtr<nsConnectionEntry> &, void *);
     bool     ProcessPendingQForEntry(nsConnectionEntry *);
     bool     AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
     void     GetConnection(nsConnectionEntry *, nsHttpTransaction *,
                            bool, nsHttpConnection **);
-    nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
+    nsresult DispatchTransaction(nsConnectionEntry *, nsHttpTransaction *,
                                  PRUint8 caps, nsHttpConnection *);
     bool     BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
     nsresult ProcessNewTransaction(nsHttpTransaction *);
     nsresult EnsureSocketThreadTargetIfOnline();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     StartedConnect();
     void     RecvdConnect();
-    
+
+    // Manage the preferred spdy connection entry for this address
+    nsConnectionEntry *GetSpdyPreferred(nsConnectionEntry *aOriginalEntry);
+    void               SetSpdyPreferred(nsConnectionEntry *ent);
+    void               RemoveSpdyPreferred(nsACString &aDottedDecimal);
+    nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
+    nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
+    nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
+                                             nsHttpConnection *conn,
+                                             nsHttpTransaction *trans);
+
+    void               ProcessSpdyPendingQ(nsConnectionEntry *ent);
+    void               ProcessSpdyPendingQ();
+    static PLDHashOperator ProcessSpdyPendingQCB(
+        const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
+        void *closure);
+
     // message handlers have this signature
     typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(PRInt32, void *);
 
     // nsConnEvent
     //
     // subclass of nsRunnable used to marshall events to the socket transport
     // thread.  this class is used to implement PostEvent.
     //
@@ -356,11 +408,18 @@ private:
 
     //
     // the connection table
     //
     // this table is indexed by connection key.  each entry is a
     // nsConnectionEntry object.
     //
     nsClassHashtable<nsCStringHashKey, nsConnectionEntry> mCT;
+
+    // this table is protected by the monitor
+    nsCStringHashSet mAlternateProtocolHash;
+    static PLDHashOperator TrimAlternateProtocolHash(PLDHashTable *table,
+                                                     PLDHashEntryHdr *hdr,
+                                                     PRUint32 number,
+                                                     void *closure);
 };
 
 #endif // !nsHttpConnectionMgr_h__
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -169,16 +169,17 @@ nsHttpHandler::nsHttpHandler()
     : mConnMgr(nsnull)
     , mHttpVersion(NS_HTTP_VERSION_1_1)
     , mProxyHttpVersion(NS_HTTP_VERSION_1_1)
     , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
     , mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
     , mReferrerLevel(0xff) // by default we always send a referrer
     , mFastFallbackToIPv4(false)
     , mIdleTimeout(10)
+    , mSpdyTimeout(180)
     , mMaxRequestAttempts(10)
     , mMaxRequestDelay(10)
     , mIdleSynTimeout(250)
     , mMaxConnections(24)
     , mMaxConnectionsPerServer(8)
     , mMaxPersistentConnectionsPerServer(2)
     , mMaxPersistentConnectionsPerProxy(4)
     , mMaxPipelinedRequests(2)
@@ -193,16 +194,19 @@ nsHttpHandler::nsHttpHandler()
     , mLegacyAppVersion("5.0")
     , mProduct("Gecko")
     , mUserAgentIsDirty(true)
     , mUseCache(true)
     , mPromptTempRedirect(true)
     , mSendSecureXSiteReferrer(true)
     , mEnablePersistentHttpsCaching(false)
     , mDoNotTrackEnabled(false)
+    , mEnableSpdy(false)
+    , mCoalesceSpdy(true)
+    , mUseAlternateProtocol(false)
 {
 #if defined(PR_LOGGING)
     gHttpLog = PR_NewLogModule("nsHttp");
 #endif
 
     LOG(("Creating nsHttpHandler [this=%x].\n", this));
 
     NS_ASSERTION(!gHttpHandler, "HTTP handler already created!");
@@ -1079,16 +1083,41 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     }
 
     if (PREF_CHANGED(HTTP_PREF("phishy-userpass-length"))) {
         rv = prefs->GetIntPref(HTTP_PREF("phishy-userpass-length"), &val);
         if (NS_SUCCEEDED(rv))
             mPhishyUserPassLength = (PRUint8) clamped(val, 0, 0xff);
     }
 
+    if (PREF_CHANGED(HTTP_PREF("spdy.enabled"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled"), &cVar);
+        if (NS_SUCCEEDED(rv))
+            mEnableSpdy = cVar;
+    }
+
+    if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.coalesce-hostnames"), &cVar);
+        if (NS_SUCCEEDED(rv))
+            mCoalesceSpdy = cVar;
+    }
+
+    if (PREF_CHANGED(HTTP_PREF("spdy.use-alternate-protocol"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("spdy.use-alternate-protocol"),
+                                &cVar);
+        if (NS_SUCCEEDED(rv))
+            mUseAlternateProtocol = cVar;
+    }
+
+    if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
+        rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val);
+        if (NS_SUCCEEDED(rv))
+            mSpdyTimeout = (PRUint16) clamped(val, 1, 0xffff);
+    }
+
     //
     // INTL options
     //
 
     if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
         nsCOMPtr<nsIPrefLocalizedString> pls;
         prefs->GetComplexValue(INTL_ACCEPT_LANGUAGES,
                                 NS_GET_IID(nsIPrefLocalizedString),
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -96,27 +96,32 @@ public:
     const nsAFlatCString &UserAgent();
 
     nsHttpVersion  HttpVersion()             { return mHttpVersion; }
     nsHttpVersion  ProxyHttpVersion()        { return mProxyHttpVersion; }
     PRUint8        ReferrerLevel()           { return mReferrerLevel; }
     bool           SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
     PRUint8        RedirectionLimit()        { return mRedirectionLimit; }
     PRUint16       IdleTimeout()             { return mIdleTimeout; }
+    PRUint16       SpdyTimeout()             { return mSpdyTimeout; }
     PRUint16       MaxRequestAttempts()      { return mMaxRequestAttempts; }
     const char    *DefaultSocketType()       { return mDefaultSocketType.get(); /* ok to return null */ }
     nsIIDNService *IDNConverter()            { return mIDNConverter; }
     PRUint32       PhishyUserPassLength()    { return mPhishyUserPassLength; }
     PRUint8        GetQoSBits()              { return mQoSBits; }
     PRUint16       GetIdleSynTimeout()       { return mIdleSynTimeout; }
     bool           FastFallbackToIPv4()      { return mFastFallbackToIPv4; }
     PRUint32       MaxSocketCount();
 
     bool           IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
 
+    bool           IsSpdyEnabled() { return mEnableSpdy; }
+    bool           CoalesceSpdy() { return mCoalesceSpdy; }
+    bool           UseAlternateProtocol() { return mUseAlternateProtocol; }
+
     bool           PromptTempRedirect()      { return mPromptTempRedirect; }
 
     nsHttpAuthCache     *AuthCache() { return &mAuthCache; }
     nsHttpConnectionMgr *ConnMgr()   { return mConnMgr; }
 
     // cache support
     nsresult GetCacheSession(nsCacheStoragePolicy, nsICacheSession **);
     PRUint32 GenerateUniqueID() { return ++mLastUniqueID; }
@@ -259,16 +264,17 @@ private:
     PRUint8  mProxyHttpVersion;
     PRUint8  mCapabilities;
     PRUint8  mProxyCapabilities;
     PRUint8  mReferrerLevel;
 
     bool mFastFallbackToIPv4;
 
     PRUint16 mIdleTimeout;
+    PRUint16 mSpdyTimeout;
     PRUint16 mMaxRequestAttempts;
     PRUint16 mMaxRequestDelay;
     PRUint16 mIdleSynTimeout;
 
     PRUint16 mMaxConnections;
     PRUint8  mMaxConnectionsPerServer;
     PRUint8  mMaxPersistentConnectionsPerServer;
     PRUint8  mMaxPersistentConnectionsPerProxy;
@@ -326,16 +332,21 @@ private:
     // if true allow referrer headers between secure non-matching hosts
     bool           mSendSecureXSiteReferrer;
 
     // Persistent HTTPS caching flag
     bool           mEnablePersistentHttpsCaching;
 
     // For broadcasting the preference to not be tracked
     bool           mDoNotTrackEnabled;
+    
+    // Try to use SPDY features instead of HTTP/1.1 over SSL
+    bool           mEnableSpdy;
+    bool           mCoalesceSpdy;
+    bool           mUseAlternateProtocol;
 };
 
 //-----------------------------------------------------------------------------
 
 extern nsHttpHandler *gHttpHandler;
 
 //-----------------------------------------------------------------------------
 // nsHttpsHandler - thin wrapper to distinguish the HTTP handler from the
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -123,17 +123,17 @@ nsHttpPipeline::AddTransaction(nsAHttpTr
 
     NS_ADDREF(trans);
     mRequestQ.AppendElement(trans);
 
     if (mConnection) {
         trans->SetConnection(this);
 
         if (mRequestQ.Length() == 1)
-            mConnection->ResumeSend();
+            mConnection->ResumeSend(trans);
     }
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpPipeline::nsISupports
 //-----------------------------------------------------------------------------
@@ -162,29 +162,29 @@ nsHttpPipeline::OnHeadersAvailable(nsAHt
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(mConnection, "no connection");
 
     // trans has now received its response headers; forward to the real connection
     return mConnection->OnHeadersAvailable(trans, requestHead, responseHead, reset);
 }
 
 nsresult
-nsHttpPipeline::ResumeSend()
+nsHttpPipeline::ResumeSend(nsAHttpTransaction *trans)
 {
     if (mConnection)
-        return mConnection->ResumeSend();
+        return mConnection->ResumeSend(trans);
     return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
-nsHttpPipeline::ResumeRecv()
+nsHttpPipeline::ResumeRecv(nsAHttpTransaction *trans)
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     NS_ASSERTION(mConnection, "no connection");
-    return mConnection->ResumeRecv();
+    return mConnection->ResumeRecv(trans);
 }
 
 void
 nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     LOG(("nsHttpPipeline::CloseTransaction [this=%x trans=%x reason=%x]\n",
         this, trans, reason));
 
@@ -369,16 +369,25 @@ nsHttpPipeline::SetConnection(nsAHttpCon
 
     NS_IF_ADDREF(mConnection = conn);
 
     PRInt32 i, count = mRequestQ.Length();
     for (i=0; i<count; ++i)
         Request(i)->SetConnection(this);
 }
 
+nsAHttpConnection *
+nsHttpPipeline::Connection()
+{
+    LOG(("nsHttpPipeline::Connection [this=%x conn=%x]\n", this, mConnection));
+
+    NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
+    return mConnection;
+}
+
 void
 nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
                                      nsIEventTarget        **target)
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     // return security callbacks from first request
     nsAHttpTransaction *trans = Request(0);
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -300,16 +300,22 @@ nsHttpTransaction::Init(PRUint8 caps,
                      nsIOService::gDefaultSegmentCount,
                      nsIOService::gBufferCache);
     if (NS_FAILED(rv)) return rv;
 
     NS_ADDREF(*responseBody = mPipeIn);
     return NS_OK;
 }
 
+nsAHttpConnection *
+nsHttpTransaction::Connection()
+{
+    return mConnection;
+}
+
 nsHttpResponseHead *
 nsHttpTransaction::TakeResponseHead()
 {
     if (!mHaveAllHeaders) {
         NS_WARNING("response headers not available or incomplete");
         return nsnull;
     }
 
@@ -1282,30 +1288,30 @@ NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHt
 // nsHttpTransaction::nsIInputStreamCallback
 //-----------------------------------------------------------------------------
 
 // called on the socket thread
 NS_IMETHODIMP
 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
 {
     if (mConnection) {
-        nsresult rv = mConnection->ResumeSend();
+        nsresult rv = mConnection->ResumeSend(this);
         if (NS_FAILED(rv))
             NS_ERROR("ResumeSend failed");
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpTransaction::nsIOutputStreamCallback
 //-----------------------------------------------------------------------------
 
 // called on the socket thread
 NS_IMETHODIMP
 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
 {
     if (mConnection) {
-        nsresult rv = mConnection->ResumeRecv();
+        nsresult rv = mConnection->ResumeRecv(this);
         if (NS_FAILED(rv))
             NS_ERROR("ResumeRecv failed");
     }
     return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -115,28 +115,27 @@ public:
     // attributes
     PRUint8                Caps()           { return mCaps; }
     nsHttpConnectionInfo  *ConnectionInfo() { return mConnInfo; }
     nsHttpResponseHead    *ResponseHead()   { return mHaveAllHeaders ? mResponseHead : nsnull; }
     nsISupports           *SecurityInfo()   { return mSecurityInfo; }
 
     nsIInterfaceRequestor *Callbacks()      { return mCallbacks; } 
     nsIEventTarget        *ConsumerTarget() { return mConsumerTarget; }
-    nsAHttpConnection     *Connection()     { return mConnection; }
 
     // Called to take ownership of the response headers; the transaction
     // will drop any reference to the response headers after this call.
     nsHttpResponseHead *TakeResponseHead();
 
     // Called to find out if the transaction generated a complete response.
     bool ResponseIsComplete() { return mResponseIsComplete; }
 
     bool      SSLConnectFailed() { return mSSLConnectFailed; }
 
-    // These methods may only be used by the connection manager.
+    // SetPriority() may only be used by the connection manager.
     void    SetPriority(PRInt32 priority) { mPriority = priority; }
     PRInt32    Priority()                 { return mPriority; }
 
     const TimingStruct& Timings() const { return mTimings; }
 
 private:
     nsresult Restart();
     char    *LocateHttpStart(char *buf, PRUint32 len,
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -48,17 +48,17 @@ interface nsIAsyncInputStream;
 interface nsIAsyncOutputStream;
 interface nsIURI;
 interface nsIProxyInfo;
 
 /**
  * The callback interface for nsIHttpChannelInternal::HTTPUpgrade()
  */
 
-[scriptable, uuid(5644af88-09e1-4fbd-83da-f012b3b30180)]
+[scriptable, uuid(4b967b6d-cd1c-49ae-a457-23ff76f5a2e8)]
 interface nsIHttpUpgradeListener : nsISupports
 {
     void onTransportAvailable(in nsISocketTransport   aTransport,
                               in nsIAsyncInputStream  aSocketIn,
                               in nsIAsyncOutputStream aSocketOut);
 };
 
 /**
@@ -176,9 +176,16 @@ interface nsIHttpChannelInternal : nsISu
      * @param aProtocolName
      *        The value of the HTTP Upgrade request header
      * @param aListener
      *        The callback object used to handle a successful upgrade
      */
     void HTTPUpgrade(in ACString aProtocolName,
                      in nsIHttpUpgradeListener aListener);
 
+    /**
+     * Enable/Disable Spdy negotiation on per channel basis.
+     * The network.http.spdy.enabled preference is still a pre-requisite
+     * for starting spdy.
+     */
+    attribute boolean allowSpdy;
+
 };
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -37,16 +37,52 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIInterfaceRequestor;
 
-[scriptable, uuid(a092097c-8386-4f1b-97b1-90eb70008c2d)]
+%{C++
+#include "nsTArray.h"
+class nsCString;
+%}
+[ref] native nsCStringTArrayRef(nsTArray<nsCString>);
+
+[scriptable, uuid(753f0f13-681d-4de3-a6c6-11aa7e0b3afd)]
 interface nsISSLSocketControl : nsISupports {
     attribute nsIInterfaceRequestor     notificationCallbacks;
 
     void proxyStartSSL();
     void StartTLS();
+
+    /* NPN (Next Protocol Negotiation) is a mechanism for
+       negotiating the protocol to be spoken inside the SSL
+       tunnel during the SSL handshake. The NPNList is the list
+       of offered client side protocols. setNPNList() needs to
+       be called before any data is read or written (including the
+       handshake to be setup correctly. */
+
+    [noscript] void setNPNList(in nsCStringTArrayRef aNPNList);
+
+    /* negotiatedNPN is '' if no NPN list was provided by the client,
+     * or if the server did not select any protocol choice from that
+     * list. That also includes the case where the server does not
+     * implement NPN.
+     *
+     * If negotiatedNPN is read before NPN has progressed to the point
+     * where this information is available NS_ERROR_NOT_CONNECTED is
+     * raised.
+     */
+    readonly attribute ACString negotiatedNPN;
+
+    /* Determine if a potential SSL connection to hostname:port with
+     * a desired NPN negotiated protocol of npnProtocol can use the socket
+     * associated with this object instead of making a new one.
+     */
+    boolean joinConnection(
+      in ACString npnProtocol, /* e.g. "spdy/2" */
+      in ACString hostname,
+      in long port);
+    
 };
 
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -2,16 +2,19 @@
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
                            .getService(Components.interfaces.nsIIOService);
 
 
+// Run by: cd objdir;  make -C netwerk/test/ xpcshell-tests    
+// or: cd objdir; make SOLO_FILE="test_URIs.js" -C netwerk/test/ check-one
+
 // Relevant RFCs: 1738, 1808, 2396, 3986 (newer than the code)
 // http://greenbytes.de/tech/webdav/rfc3986.html#rfc.section.5.4
 // http://greenbytes.de/tech/tc/uris/
 
 // TEST DATA
 // ---------
 var gTests = [
   { spec:    "about:blank",
@@ -111,16 +114,34 @@ var gTests = [
     scheme:  "ftp",
     prePath: "ftp://foo:bar@ftp.mozilla.org:100",
     port:    100,
     username: "foo",
     password: "bar",
     path:    "/pub/mozilla.org/README",
     ref:     "",
     nsIURL:  true, nsINestedURI: false },
+  { spec:    "ftp://foo:@ftp.mozilla.org:100/pub/mozilla.org/README",
+    scheme:  "ftp",
+    prePath: "ftp://foo:@ftp.mozilla.org:100",
+    port:    100,
+    username: "foo",
+    password: "",
+    path:    "/pub/mozilla.org/README",
+    ref:     "",
+    nsIURL:  true, nsINestedURI: false },
+  //Bug 706249
+  { spec:    "http:x:@",
+    scheme:  "http",
+    prePath: "http://x:@",
+    username: "x",
+    password: "",
+    path:    "",
+    ref:     "",
+    nsIURL:  true, nsINestedURI: false },
   { spec:    "gopher://mozilla.org/",
     scheme:  "gopher",
     prePath: "gopher:",
     path:    "//mozilla.org/",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "http://",
     scheme:  "http",
--- a/security/coreconf/coreconf.dep
+++ b/security/coreconf/coreconf.dep
@@ -37,8 +37,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/manager/ssl/src/Makefile.in
+++ b/security/manager/ssl/src/Makefile.in
@@ -55,23 +55,23 @@ LIBXUL_LIBRARY	= 1
 
 CPPSRCS = 				\
 	nsCERTValInParamWrapper.cpp     \
 	nsNSSCleaner.cpp                \
 	nsCertOverrideService.cpp   \
 	nsRecentBadCerts.cpp \
         nsClientAuthRemember.cpp        \
 	nsPSMBackgroundThread.cpp       \
-	nsSSLThread.cpp                 \
 	nsCertVerificationThread.cpp    \
 	nsProtectedAuthThread.cpp \
 	nsNSSCallbacks.cpp		\
 	nsNSSComponent.cpp		\
 	nsNSSErrors.cpp			\
 	nsNSSIOLayer.cpp		\
+	SSLServerCertVerification.cpp   \
 	nsSSLStatus.cpp		\
 	nsNSSModule.cpp			\
 	nsSSLSocketProvider.cpp		\
 	nsTLSSocketProvider.cpp		\
 	nsSDR.cpp			\
 	nsPK11TokenDB.cpp		\
 	nsNSSCertificate.cpp		\
 	nsPKCS12Blob.cpp                \
copy from security/manager/ssl/src/nsNSSCallbacks.cpp
copy to security/manager/ssl/src/SSLServerCertVerification.cpp
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -35,992 +35,254 @@
  * 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 "nsNSSComponent.h" // for PIPNSS string bundle calls.
-#include "nsNSSCallbacks.h"
+
+/* 
+ * All I/O is done on the socket transport thread, including all calls into
+ * libssl. That is, all SSL_* functions must be called on the socket transport
+ * thread. This also means that all SSL callback functions will be called on
+ * the socket transport thread, including in particular the auth certificate
+ * hook.
+ *
+ * During certificate authentication, we call CERT_PKIXVerifyCert or
+ * CERT_VerifyCert. These functions may make zero or more HTTP requests
+ * for OCSP responses, CRLs, intermediate certificates, etc.
+ *
+ * If our cert auth hook were to call the CERT_*Verify* functions directly,
+ * there would be a deadlock: The CERT_*Verify* function would cause an event
+ * to be asynchronously posted to the socket transport thread, and then it
+ * would block the socket transport thread waiting to be notified of the HTTP
+ * response. However, the HTTP request would never actually be processed
+ * because the socket transport thread would be blocked and so it wouldn't be
+ * able process HTTP requests. (i.e. Deadlock.)
+ *
+ * Consequently, we must always call the CERT_*Verify* cert functions off the
+ * socket transport thread. To accomplish this, our auth cert hook dispatches a
+ * SSLServerCertVerificationJob to a pool of background threads, and then
+ * immediatley return SECWouldBlock to libssl. These jobs are where the
+ * CERT_*Verify* functions are actually called. 
+ *
+ * When our auth cert hook returns SECWouldBlock, libssl will carry on the
+ * handshake while we validate the certificate. This will free up the socket
+ * transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
+ * requests needed for cert verification as mentioned above--can be processed.
+ *
+ * Once the CERT_*Verify* function returns, the cert verification job
+ * dispatches a SSLServerCertVerificationResult to the socket transport thread;
+ * the SSLServerCertVerificationResult will notify libssl that the certificate
+ * authentication is complete. Once libssl is notified that the authentication
+ * is complete, it will continue the SSL handshake (if it hasn't already
+ * finished) and it will begin allowing us to send/receive data on the
+ * connection.
+ *
+ * Timeline of events:
+ *
+ *    * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
+ *      transport thread.
+ *    * SSLServerCertVerificationJob::Dispatch queues a job
+ *      (instance of SSLServerCertVerificationJob) to its background thread
+ *      pool and returns.
+ *    * One of the background threads calls CERT_*Verify*, which may enqueue
+ *      some HTTP request(s) onto the socket transport thread, and then
+ *      blocks that background thread waiting for the responses and/or timeouts
+ *      or errors for those requests.
+ *    * Once those HTTP responses have all come back or failed, the
+ *      CERT_*Verify* function returns a result indicating that the validation
+ *      succeeded or failed.
+ *    * If the validation succeeded, then a SSLServerCertVerificationResult
+ *      event is posted to the socket transport thread, and the cert
+ *      verification thread becomes free to verify other certificates.
+ *    * Otherwise, a CertErrorRunnable is posted to the socket transport thread
+ *      and then to the main thread (blocking both, see CertErrorRunnable) to
+ *      do cert override processing and bad cert listener notification. Then
+ *      the cert verification thread becomes free to verify other certificates.
+ *    * After processing cert overrides, the CertErrorRunnable will dispatch a
+ *      SSLServerCertVerificationResult event to the socket transport thread to
+ *      notify it of the result of the override processing; then it returns,
+ *      freeing up the main thread.
+ *    * The SSLServerCertVerificationResult event will either wake up the 
+ *      socket (using SSL_RestartHandshakeAfterServerCert) if validation
+ *      succeeded or there was an error override, or it will set an error flag
+ *      so that the next I/O operation on the socket will fail, causing the
+ *      socket transport thread to close the connection.
+ *
+ * Cert override processing must happen on the main thread because it accesses
+ * the nsICertOverrideService, and that service must be accessed on the main 
+ * thread because some extensions (Selenium, in particular) replace it with a
+ * Javascript implementation, and chrome JS must always be run on the main
+ * thread.
+ *
+ * SSLServerCertVerificationResult must be dispatched to the socket transport
+ * thread because we must only call SSL_* functions on the socket transport
+ * thread since they may do I/O, because many parts of nsNSSSocketInfo and
+ * the PSM NSS I/O layer are not thread-safe, and because we need the event to
+ * interrupt the PR_Poll that may waiting for I/O on the socket for which we
+ * are validating the cert.
+ */
+
+#include "SSLServerCertVerification.h"
+#include "nsNSSComponent.h"
 #include "nsNSSCertificate.h"
-#include "nsNSSCleaner.h"
-#include "nsSSLStatus.h"
-#include "nsNSSIOLayer.h" // for nsNSSSocketInfo
-#include "nsIWebProgressListener.h"
-#include "nsIStringBundle.h"
-#include "nsXPIDLString.h"
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsIServiceManager.h"
-#include "nsReadableUtils.h"
-#include "nsIPrompt.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsProtectedAuthThread.h"
-#include "nsITokenDialogs.h"
-#include "nsCRT.h"
-#include "nsNSSShutDown.h"
-#include "nsIUploadChannel.h"
-#include "nsSSLThread.h"
-#include "nsThreadUtils.h"
-#include "nsIThread.h"
-#include "nsIWindowWatcher.h"
-#include "nsIPrompt.h"
-#include "nsProxyRelease.h"
-#include "PSMRunnable.h"
-#include "nsIConsoleService.h"
+#include "nsNSSIOLayer.h"
+
+#include "nsIThreadPool.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
 
 #include "ssl.h"
-#include "cert.h"
-#include "ocsp.h"
-#include "nssb64.h"
 #include "secerr.h"
 #include "sslerr.h"
 
-using namespace mozilla;
-using namespace mozilla::psm;
-
-static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
-
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
 
-class nsHTTPDownloadEvent : public nsRunnable {
-public:
-  nsHTTPDownloadEvent();
-  ~nsHTTPDownloadEvent();
-
-  NS_IMETHOD Run();
-
-  nsNSSHttpRequestSession *mRequestSession;
-  
-  nsCOMPtr<nsHTTPListener> mListener;
-  bool mResponsibleForDoneSignal;
-};
+namespace mozilla { namespace psm {
 
-nsHTTPDownloadEvent::nsHTTPDownloadEvent()
-:mResponsibleForDoneSignal(true)
-{
-}
-
-nsHTTPDownloadEvent::~nsHTTPDownloadEvent()
-{
-  if (mResponsibleForDoneSignal && mListener)
-    mListener->send_done_signal();
-
-  mRequestSession->Release();
-}
-
-NS_IMETHODIMP
-nsHTTPDownloadEvent::Run()
-{
-  if (!mListener)
-    return NS_OK;
+namespace {
+// do not use a nsCOMPtr to avoid static initializer/destructor
+nsIThreadPool * gCertVerificationThreadPool = nsnull;
+} // unnamed namespace
 
-  nsresult rv;
-
-  nsCOMPtr<nsIIOService> ios = do_GetIOService();
-  NS_ENSURE_STATE(ios);
-
-  nsCOMPtr<nsIChannel> chan;
-  ios->NewChannel(mRequestSession->mURL, nsnull, nsnull, getter_AddRefs(chan));
-  NS_ENSURE_STATE(chan);
-
-  chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
-
-  // Create a loadgroup for this new channel.  This way if the channel
-  // is redirected, we'll have a way to cancel the resulting channel.
-  nsCOMPtr<nsILoadGroup> lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
-  chan->SetLoadGroup(lg);
-
-  if (mRequestSession->mHasPostData)
-  {
-    nsCOMPtr<nsIInputStream> uploadStream;
-    rv = NS_NewPostDataStream(getter_AddRefs(uploadStream),
-                              false,
-                              mRequestSession->mPostData,
-                              0, ios);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(chan));
-    NS_ENSURE_STATE(uploadChannel);
-
-    rv = uploadChannel->SetUploadStream(uploadStream, 
-                                        mRequestSession->mPostContentType,
-                                        -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+// Called when the socket transport thread starts, to initialize the SSL cert
+// verification thread pool. By tying the thread pool startup/shutdown directly
+// to the STS thread's lifetime, we ensure that they are *always* available for
+// SSL connections and that there are no races during startup and especially
+// shutdown. (Previously, we have had multiple problems with races in PSM
+// background threads, and the race-prevention/shutdown logic used there is
+// brittle. Since this service is critical to things like downloading updates,
+// we take no chances.) Also, by doing things this way, we avoid the need for
+// locks, since gCertVerificationThreadPool is only ever accessed on the socket
+// transport thread.
+void
+InitializeSSLServerCertVerificationThreads()
+{
+  // TODO: tuning, make parameters preferences
+  // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
+  // Currently, the nsThreadPool.h header isn't exported for us to do so.
+  nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
+                                   &gCertVerificationThreadPool);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create SSL cert verification threads.");
+    return;
   }
 
-  nsCOMPtr<nsIHttpChannel> hchan = do_QueryInterface(chan);
-  NS_ENSURE_STATE(hchan);
-
-  rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mResponsibleForDoneSignal = false;
-  mListener->mResponsibleForDoneSignal = true;
-
-  mListener->mLoadGroup = lg.get();
-  NS_ADDREF(mListener->mLoadGroup);
-  mListener->mLoadGroupOwnerThread = PR_GetCurrentThread();
-
-  rv = NS_NewStreamLoader(getter_AddRefs(mListener->mLoader), 
-                          mListener);
-
-  if (NS_SUCCEEDED(rv))
-    rv = hchan->AsyncOpen(mListener->mLoader, nsnull);
-
-  if (NS_FAILED(rv)) {
-    mListener->mResponsibleForDoneSignal = false;
-    mResponsibleForDoneSignal = true;
-
-    NS_RELEASE(mListener->mLoadGroup);
-    mListener->mLoadGroup = nsnull;
-    mListener->mLoadGroupOwnerThread = nsnull;
-  }
-
-  return NS_OK;
-}
-
-struct nsCancelHTTPDownloadEvent : nsRunnable {
-  nsCOMPtr<nsHTTPListener> mListener;
-
-  NS_IMETHOD Run() {
-    mListener->FreeLoadGroup(true);
-    mListener = nsnull;
-    return NS_OK;
-  }
-};
-
-SECStatus nsNSSHttpServerSession::createSessionFcn(const char *host,
-                                                   PRUint16 portnum,
-                                                   SEC_HTTP_SERVER_SESSION *pSession)
-{
-  if (!host || !pSession)
-    return SECFailure;
-
-  nsNSSHttpServerSession *hss = new nsNSSHttpServerSession;
-  if (!hss)
-    return SECFailure;
-
-  hss->mHost = host;
-  hss->mPort = portnum;
-
-  *pSession = hss;
-  return SECSuccess;
+  (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
+  (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
+  (void) gCertVerificationThreadPool->SetThreadLimit(5);
 }
 
-SECStatus nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session,
-                                             const char *http_protocol_variant,
-                                             const char *path_and_query_string,
-                                             const char *http_request_method, 
-                                             const PRIntervalTime timeout, 
-                                             SEC_HTTP_REQUEST_SESSION *pRequest)
-{
-  if (!session || !http_protocol_variant || !path_and_query_string || 
-      !http_request_method || !pRequest)
-    return SECFailure;
-
-  nsNSSHttpServerSession* hss = static_cast<nsNSSHttpServerSession*>(session);
-  if (!hss)
-    return SECFailure;
-
-  nsNSSHttpRequestSession *rs = new nsNSSHttpRequestSession;
-  if (!rs)
-    return SECFailure;
-
-  rs->mTimeoutInterval = timeout;
-
-  // Use a maximum timeout value of 10 seconds because of bug 404059.
-  // FIXME: Use a better approach once 406120 is ready.
-  PRUint32 maxBug404059Timeout = PR_TicksPerSecond() * 10;
-  if (timeout > maxBug404059Timeout) {
-    rs->mTimeoutInterval = maxBug404059Timeout;
-  }
-
-  rs->mURL.Assign(http_protocol_variant);
-  rs->mURL.AppendLiteral("://");
-  rs->mURL.Append(hss->mHost);
-  rs->mURL.AppendLiteral(":");
-  rs->mURL.AppendInt(hss->mPort);
-  rs->mURL.Append(path_and_query_string);
-
-  rs->mRequestMethod = http_request_method;
-
-  *pRequest = (void*)rs;
-  return SECSuccess;
-}
-
-SECStatus nsNSSHttpRequestSession::setPostDataFcn(const char *http_data, 
-                                                  const PRUint32 http_data_len,
-                                                  const char *http_content_type)
-{
-  mHasPostData = true;
-  mPostData.Assign(http_data, http_data_len);
-  mPostContentType.Assign(http_content_type);
-
-  return SECSuccess;
-}
-
-SECStatus nsNSSHttpRequestSession::addHeaderFcn(const char *http_header_name, 
-                                                const char *http_header_value)
+// Called when the socket transport thread finishes, to destroy the thread
+// pool. Since the socket transport service has stopped processing events, it
+// will not attempt any more SSL I/O operations, so it is clearly safe to shut
+// down the SSL cert verification infrastructure. Also, the STS will not
+// dispatch many SSL verification result events at this point, so any pending
+// cert verifications will (correctly) fail at the point they are dispatched.
+//
+// The other shutdown race condition that is possible is a race condition with
+// shutdown of the nsNSSComponent service. We use the
+// nsNSSShutdownPreventionLock where needed (not here) to prevent that.
+void StopSSLServerCertVerificationThreads()
 {
-  return SECFailure; // not yet implemented
-
-  // All http code needs to be postponed to the UI thread.
-  // Once this gets implemented, we need to add a string list member to
-  // nsNSSHttpRequestSession and queue up the headers,
-  // so they can be added in HandleHTTPDownloadPLEvent.
-  //
-  // The header will need to be set using 
-  //   mHttpChannel->SetRequestHeader(nsDependentCString(http_header_name), 
-  //                                  nsDependentCString(http_header_value), 
-  //                                  false)));
-}
-
-SECStatus nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc **pPollDesc,
-                                                        PRUint16 *http_response_code, 
-                                                        const char **http_response_content_type, 
-                                                        const char **http_response_headers, 
-                                                        const char **http_response_data, 
-                                                        PRUint32 *http_response_data_len)
-{
-  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-         ("nsNSSHttpRequestSession::trySendAndReceiveFcn to %s\n", mURL.get()));
-
-  const int max_retries = 2;
-  int retry_count = 0;
-  bool retryable_error = false;
-  SECStatus result_sec_status = SECFailure;
-
-  do
-  {
-    if (retry_count > 0)
-    {
-      if (retryable_error)
-      {
-        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-               ("nsNSSHttpRequestSession::trySendAndReceiveFcn - sleeping and retrying: %d of %d\n",
-                retry_count, max_retries));
-      }
-
-      PR_Sleep( PR_MillisecondsToInterval(300) * retry_count );
-    }
-
-    ++retry_count;
-    retryable_error = false;
-
-    result_sec_status =
-      internal_send_receive_attempt(retryable_error, pPollDesc, http_response_code,
-                                    http_response_content_type, http_response_headers,
-                                    http_response_data, http_response_data_len);
-  }
-  while (retryable_error &&
-         retry_count < max_retries);
-
-#ifdef PR_LOGGING
-  if (retry_count > 1)
-  {
-    if (retryable_error)
-      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("nsNSSHttpRequestSession::trySendAndReceiveFcn - still failing, giving up...\n"));
-    else
-      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("nsNSSHttpRequestSession::trySendAndReceiveFcn - success at attempt %d\n",
-              retry_count));
-  }
-#endif
-
-  return result_sec_status;
-}
-
-void
-nsNSSHttpRequestSession::AddRef()
-{
-  NS_AtomicIncrementRefcnt(mRefCount);
-}
-
-void
-nsNSSHttpRequestSession::Release()
-{
-  PRInt32 newRefCount = NS_AtomicDecrementRefcnt(mRefCount);
-  if (!newRefCount) {
-    delete this;
+  if (gCertVerificationThreadPool) {
+    gCertVerificationThreadPool->Shutdown();
+    NS_RELEASE(gCertVerificationThreadPool);
   }
 }
 
-SECStatus
-nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error,
-                                                       PRPollDesc **pPollDesc,
-                                                       PRUint16 *http_response_code,
-                                                       const char **http_response_content_type,
-                                                       const char **http_response_headers,
-                                                       const char **http_response_data,
-                                                       PRUint32 *http_response_data_len)
-{
-  if (pPollDesc) *pPollDesc = nsnull;
-  if (http_response_code) *http_response_code = 0;
-  if (http_response_content_type) *http_response_content_type = 0;
-  if (http_response_headers) *http_response_headers = 0;
-  if (http_response_data) *http_response_data = 0;
-
-  PRUint32 acceptableResultSize = 0;
-
-  if (http_response_data_len)
-  {
-    acceptableResultSize = *http_response_data_len;
-    *http_response_data_len = 0;
-  }
-  
-  if (!mListener)
-    return SECFailure;
-
-  Mutex& waitLock = mListener->mLock;
-  CondVar& waitCondition = mListener->mCondition;
-  volatile bool &waitFlag = mListener->mWaitFlag;
-  waitFlag = true;
-
-  nsRefPtr<nsHTTPDownloadEvent> event = new nsHTTPDownloadEvent;
-  if (!event)
-    return SECFailure;
-
-  event->mListener = mListener;
-  this->AddRef();
-  event->mRequestSession = this;
+namespace {
 
-  nsresult rv = NS_DispatchToMainThread(event);
-  if (NS_FAILED(rv))
-  {
-    event->mResponsibleForDoneSignal = false;
-    return SECFailure;
-  }
-
-  bool request_canceled = false;
-
-  {
-    MutexAutoLock locker(waitLock);
-
-    const PRIntervalTime start_time = PR_IntervalNow();
-    PRIntervalTime wait_interval;
-
-    bool running_on_main_thread = NS_IsMainThread();
-    if (running_on_main_thread)
-    {
-      // let's process events quickly
-      wait_interval = PR_MicrosecondsToInterval(50);
-    }
-    else
-    { 
-      // On a secondary thread, it's fine to wait some more for
-      // for the condition variable.
-      wait_interval = PR_MillisecondsToInterval(250);
-    }
-
-    while (waitFlag)
-    {
-      if (running_on_main_thread)
-      {
-        // Networking runs on the main thread, which we happen to block here.
-        // Processing events will allow the OCSP networking to run while we 
-        // are waiting. Thanks a lot to Darin Fisher for rewriting the 
-        // thread manager. Thanks a lot to Christian Biesinger who
-        // made me aware of this possibility. (kaie)
-
-        MutexAutoUnlock unlock(waitLock);
-        NS_ProcessNextEvent(nsnull);
-      }
-
-      waitCondition.Wait(wait_interval);
-      
-      if (!waitFlag)
-        break;
+class SSLServerCertVerificationJob : public nsRunnable
+{
+public:
+  // Must be called only on the socket transport thread
+  static SECStatus Dispatch(const void * fdForLogging,
+                            nsNSSSocketInfo * infoObject,
+                            CERTCertificate * serverCert);
+private:
+  NS_DECL_NSIRUNNABLE
 
-      if (!request_canceled)
-      {
-        bool wantExit = nsSSLThread::stoppedOrStopping();
-        bool timeout = 
-          (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
-
-        if (wantExit || timeout)
-        {
-          request_canceled = true;
+  // Must be called only on the socket transport thread
+  SSLServerCertVerificationJob(const void * fdForLogging,
+                               nsNSSSocketInfo & socketInfo, 
+                               CERTCertificate & cert);
+  ~SSLServerCertVerificationJob();
 
-          nsRefPtr<nsCancelHTTPDownloadEvent> cancelevent = new nsCancelHTTPDownloadEvent;
-          cancelevent->mListener = mListener;
-          rv = NS_DispatchToMainThread(cancelevent);
-          if (NS_FAILED(rv)) {
-            NS_WARNING("cannot post cancel event");
-          }
-          break;
-        }
-      }
-    }
-  }
-
-  if (request_canceled)
-    return SECFailure;
-
-  if (NS_FAILED(mListener->mResultCode))
-  {
-    if (mListener->mResultCode == NS_ERROR_CONNECTION_REFUSED
-        ||
-        mListener->mResultCode == NS_ERROR_NET_RESET)
-    {
-      retryable_error = true;
-    }
-    return SECFailure;
-  }
-
-  if (http_response_code)
-    *http_response_code = mListener->mHttpResponseCode;
-
-  if (mListener->mHttpRequestSucceeded && http_response_data && http_response_data_len) {
+  // Runs on one of the background threads
+  SECStatus AuthCertificate(const nsNSSShutDownPreventionLock & proofOfLock);
 
-    *http_response_data_len = mListener->mResultLen;
-  
-    // acceptableResultSize == 0 means: any size is acceptable
-    if (acceptableResultSize != 0
-        &&
-        acceptableResultSize < mListener->mResultLen)
-    {
-      return SECFailure;
-    }
-
-    // return data by reference, result data will be valid 
-    // until "this" gets destroyed by NSS
-    *http_response_data = (const char*)mListener->mResultData;
-  }
-
-  if (mListener->mHttpRequestSucceeded && http_response_content_type) {
-    if (mListener->mHttpResponseContentType.Length()) {
-      *http_response_content_type = mListener->mHttpResponseContentType.get();
-    }
-  }
-
-  return SECSuccess;
-}
+  const void * const mFdForLogging;
+  const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
+  CERTCertificate * const mCert;
+};
 
-SECStatus nsNSSHttpRequestSession::cancelFcn()
-{
-  // As of today, only the blocking variant of the http interface
-  // has been implemented. Implementing cancelFcn will be necessary
-  // as soon as we implement the nonblocking variant.
-  return SECSuccess;
-}
-
-SECStatus nsNSSHttpRequestSession::freeFcn()
-{
-  Release();
-  return SECSuccess;
-}
-
-nsNSSHttpRequestSession::nsNSSHttpRequestSession()
-: mRefCount(1),
-  mHasPostData(false),
-  mTimeoutInterval(0),
-  mListener(new nsHTTPListener)
-{
-}
-
-nsNSSHttpRequestSession::~nsNSSHttpRequestSession()
+SSLServerCertVerificationJob::SSLServerCertVerificationJob(
+    const void * fdForLogging, nsNSSSocketInfo & socketInfo,
+    CERTCertificate & cert)
+  : mFdForLogging(fdForLogging)
+  , mSocketInfo(&socketInfo)
+  , mCert(CERT_DupCertificate(&cert))
 {
 }
 
-SEC_HttpClientFcn nsNSSHttpInterface::sNSSInterfaceTable;
-
-void nsNSSHttpInterface::initTable()
-{
-  sNSSInterfaceTable.version = 1;
-  SEC_HttpClientFcnV1 &v1 = sNSSInterfaceTable.fcnTable.ftable1;
-  v1.createSessionFcn = createSessionFcn;
-  v1.keepAliveSessionFcn = keepAliveFcn;
-  v1.freeSessionFcn = freeSessionFcn;
-  v1.createFcn = createFcn;
-  v1.setPostDataFcn = setPostDataFcn;
-  v1.addHeaderFcn = addHeaderFcn;
-  v1.trySendAndReceiveFcn = trySendAndReceiveFcn;
-  v1.cancelFcn = cancelFcn;
-  v1.freeFcn = freeFcn;
-}
-
-void nsNSSHttpInterface::registerHttpClient()
-{
-  SEC_RegisterDefaultHttpClient(&sNSSInterfaceTable);
-}
-
-void nsNSSHttpInterface::unregisterHttpClient()
-{
-  SEC_RegisterDefaultHttpClient(nsnull);
-}
-
-nsHTTPListener::nsHTTPListener()
-: mResultData(nsnull),
-  mResultLen(0),
-  mLock("nsHTTPListener.mLock"),
-  mCondition(mLock, "nsHTTPListener.mCondition"),
-  mWaitFlag(true),
-  mResponsibleForDoneSignal(false),
-  mLoadGroup(nsnull),
-  mLoadGroupOwnerThread(nsnull)
-{
-}
-
-nsHTTPListener::~nsHTTPListener()
-{
-  if (mResponsibleForDoneSignal)
-    send_done_signal();
-
-  if (mLoader) {
-    nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
-    NS_ProxyRelease(mainThread, mLoader);
-  }
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTTPListener, nsIStreamLoaderObserver)
-
-void
-nsHTTPListener::FreeLoadGroup(bool aCancelLoad)
-{
-  nsILoadGroup *lg = nsnull;
-
-  MutexAutoLock locker(mLock);
-
-  if (mLoadGroup) {
-    if (mLoadGroupOwnerThread != PR_GetCurrentThread()) {
-      NS_ASSERTION(false,
-                   "attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads, leaking it!");
-    }
-    else {
-      lg = mLoadGroup;
-      mLoadGroup = nsnull;
-    }
-  }
-
-  if (lg) {
-    if (aCancelLoad) {
-      lg->Cancel(NS_ERROR_ABORT);
-    }
-    NS_RELEASE(lg);
-  }
-}
-
-NS_IMETHODIMP
-nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader,
-                                 nsISupports* aContext,
-                                 nsresult aStatus,
-                                 PRUint32 stringLen,
-                                 const PRUint8* string)
+SSLServerCertVerificationJob::~SSLServerCertVerificationJob()
 {
-  mResultCode = aStatus;
-
-  FreeLoadGroup(false);
-
-  nsCOMPtr<nsIRequest> req;
-  nsCOMPtr<nsIHttpChannel> hchan;
-
-  nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
-  
-#ifdef PR_LOGGING
-  if (NS_FAILED(aStatus))
-  {
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-           ("nsHTTPListener::OnStreamComplete status failed %d", aStatus));
-  }
-#endif
-
-  if (NS_SUCCEEDED(rv))
-    hchan = do_QueryInterface(req, &rv);
-
-  if (NS_SUCCEEDED(rv))
-  {
-    rv = hchan->GetRequestSucceeded(&mHttpRequestSucceeded);
-    if (NS_FAILED(rv))
-      mHttpRequestSucceeded = false;
-
-    mResultLen = stringLen;
-    mResultData = string; // reference. Make sure loader lives as long as this
-
-    unsigned int rcode;
-    rv = hchan->GetResponseStatus(&rcode);
-    if (NS_FAILED(rv))
-      mHttpResponseCode = 500;
-    else
-      mHttpResponseCode = rcode;
-
-    hchan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"), 
-                                    mHttpResponseContentType);
-  }
-
-  if (mResponsibleForDoneSignal)
-    send_done_signal();
-  
-  return aStatus;
-}
-
-void nsHTTPListener::send_done_signal()
-{
-  mResponsibleForDoneSignal = false;
-
-  {
-    MutexAutoLock locker(mLock);
-    mWaitFlag = false;
-    mCondition.NotifyAll();
-  }
-}
-
-static char*
-ShowProtectedAuthPrompt(PK11SlotInfo* slot, nsIInterfaceRequestor *ir)
-{
-  if (!NS_IsMainThread()) {
-    NS_ERROR("ShowProtectedAuthPrompt called off the main thread");
-    return nsnull;
-  }
-
-  char* protAuthRetVal = nsnull;
-
-  // Get protected auth dialogs
-  nsITokenDialogs* dialogs = 0;
-  nsresult nsrv = getNSSDialogs((void**)&dialogs, 
-                                NS_GET_IID(nsITokenDialogs), 
-                                NS_TOKENDIALOGS_CONTRACTID);
-  if (NS_SUCCEEDED(nsrv))
-  {
-    nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread();
-    if (protectedAuthRunnable)
-    {
-      NS_ADDREF(protectedAuthRunnable);
-
-      protectedAuthRunnable->SetParams(slot);
-      
-      nsCOMPtr<nsIProtectedAuthThread> runnable = do_QueryInterface(protectedAuthRunnable);
-      if (runnable)
-      {
-        nsrv = dialogs->DisplayProtectedAuth(ir, runnable);
-              
-        // We call join on the thread,
-        // so we can be sure that no simultaneous access will happen.
-        protectedAuthRunnable->Join();
-              
-        if (NS_SUCCEEDED(nsrv))
-        {
-          SECStatus rv = protectedAuthRunnable->GetResult();
-          switch (rv)
-          {
-              case SECSuccess:
-                  protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED));
-                  break;
-              case SECWouldBlock:
-                  protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY));
-                  break;
-              default:
-                  protAuthRetVal = nsnull;
-                  break;
-              
-          }
-        }
-      }
-
-      NS_RELEASE(protectedAuthRunnable);
-    }
-
-    NS_RELEASE(dialogs);
-  }
-
-  return protAuthRetVal;
+  CERT_DestroyCertificate(mCert);
 }
 
-class PK11PasswordPromptRunnable : public SyncRunnableBase
-{
-public:
-  PK11PasswordPromptRunnable(PK11SlotInfo* slot, 
-                             nsIInterfaceRequestor* ir)
-    : mResult(nsnull),
-      mSlot(slot),
-      mIR(ir)
-  {
-  }
-  char * mResult; // out
-  virtual void RunOnTargetThread();
-private:
-  PK11SlotInfo* const mSlot; // in
-  nsIInterfaceRequestor* const mIR; // in
-};
-
-void PK11PasswordPromptRunnable::RunOnTargetThread()
-{
-  nsNSSShutDownPreventionLock locker;
-  nsresult rv = NS_OK;
-  PRUnichar *password = nsnull;
-  bool value = false;
-  nsCOMPtr<nsIPrompt> prompt;
-
-  /* TODO: Retry should generate a different dialog message */
-/*
-  if (retry)
-    return nsnull;
-*/
-
-  if (!mIR)
-  {
-    nsNSSComponent::GetNewPrompter(getter_AddRefs(prompt));
-  }
-  else
-  {
-    prompt = do_GetInterface(mIR);
-    NS_ASSERTION(prompt != nsnull, "callbacks does not implement nsIPrompt");
-  }
-
-  if (!prompt)
-    return;
-
-  if (PK11_ProtectedAuthenticationPath(mSlot)) {
-    mResult = ShowProtectedAuthPrompt(mSlot, mIR);
-    return;
-  }
-
-  nsAutoString promptString;
-  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
-
-  if (NS_FAILED(rv))
-    return; 
-
-  const PRUnichar* formatStrings[1] = { 
-    ToNewUnicode(NS_ConvertUTF8toUTF16(PK11_GetTokenName(mSlot)))
-  };
-  rv = nssComponent->PIPBundleFormatStringFromName("CertPassPrompt",
-                                      formatStrings, 1,
-                                      promptString);
-  nsMemory::Free(const_cast<PRUnichar*>(formatStrings[0]));
-
-  if (NS_FAILED(rv))
-    return;
-
-  {
-    nsPSMUITracker tracker;
-    if (tracker.isUIForbidden()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-    }
-    else {
-      // Although the exact value is ignored, we must not pass invalid
-      // bool values through XPConnect.
-      bool checkState = false;
-      rv = prompt->PromptPassword(nsnull, promptString.get(),
-                                  &password, nsnull, &checkState, &value);
-    }
-  }
-  
-  if (NS_SUCCEEDED(rv) && value) {
-    mResult = ToNewUTF8String(nsDependentString(password));
-    NS_Free(password);
-  }
-}
-
-char* PR_CALLBACK
-PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg)
-{
-  nsRefPtr<PK11PasswordPromptRunnable> runnable = 
-    new PK11PasswordPromptRunnable(slot,
-                                   static_cast<nsIInterfaceRequestor*>(arg));
-  if (NS_IsMainThread()) {
-    runnable->RunOnTargetThread();
-  } else {
-    runnable->DispatchToMainThreadAndWait();
-  }
-  return runnable->mResult;
-}
-
-void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) {
-  nsNSSShutDownPreventionLock locker;
-  PRInt32 sslStatus;
-  char* signer = nsnull;
-  char* cipherName = nsnull;
-  PRInt32 keyLength;
-  nsresult rv;
-  PRInt32 encryptBits;
-
-  if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
-                                       &encryptBits, &signer, nsnull)) {
-    return;
-  }
-
-  PRInt32 secStatus;
-  if (sslStatus == SSL_SECURITY_STATUS_OFF)
-    secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
-  else if (encryptBits >= 90)
-    secStatus = (nsIWebProgressListener::STATE_IS_SECURE |
-                 nsIWebProgressListener::STATE_SECURE_HIGH);
-  else
-    secStatus = (nsIWebProgressListener::STATE_IS_SECURE |
-                 nsIWebProgressListener::STATE_SECURE_LOW);
-
-  PRBool siteSupportsSafeRenego;
-  if (SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, &siteSupportsSafeRenego) != SECSuccess
-      || !siteSupportsSafeRenego) {
-
-    bool wantWarning = (nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() > 0);
-
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-    nsCOMPtr<nsIConsoleService> console;
-    if (infoObject && wantWarning) {
-      console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-      if (console) {
-        nsXPIDLCString hostName;
-        infoObject->GetHostName(getter_Copies(hostName));
-
-        nsAutoString msg;
-        msg.Append(NS_ConvertASCIItoUTF16(hostName));
-        msg.Append(NS_LITERAL_STRING(" : server does not support RFC 5746, see CVE-2009-3555"));
-
-        console->LogStringMessage(msg.get());
-      }
-    }
-    if (nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()) {
-      secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
-    }
-  }
-
-
-  CERTCertificate *peerCert = SSL_PeerCertificate(fd);
-  const char* caName = nsnull; // caName is a pointer only, no ownership
-  char* certOrgName = CERT_GetOrgName(&peerCert->issuer);
-  CERT_DestroyCertificate(peerCert);
-  caName = certOrgName ? certOrgName : signer;
-
-  const char* verisignName = "Verisign, Inc.";
-  // If the CA name is RSA Data Security, then change the name to the real
-  // name of the company i.e. VeriSign, Inc.
-  if (nsCRT::strcmp((const char*)caName, "RSA Data Security, Inc.") == 0) {
-    caName = verisignName;
-  }
-
-  nsAutoString shortDesc;
-  const PRUnichar* formatStrings[1] = { ToNewUnicode(NS_ConvertUTF8toUTF16(caName)) };
-  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
-  if (NS_SUCCEEDED(rv)) {
-    rv = nssComponent->PIPBundleFormatStringFromName("SignedBy",
-                                                   formatStrings, 1,
-                                                   shortDesc);
-
-    nsMemory::Free(const_cast<PRUnichar*>(formatStrings[0]));
-
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-    infoObject->SetSecurityState(secStatus);
-    infoObject->SetShortSecurityDescription(shortDesc.get());
-
-    /* Set the SSL Status information */
-    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
-    if (!status) {
-      status = new nsSSLStatus();
-      infoObject->SetSSLStatus(status);
-    }
-
-    nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
-      infoObject, status);
-
-    CERTCertificate *serverCert = SSL_PeerCertificate(fd);
-    if (serverCert) {
-      nsRefPtr<nsNSSCertificate> nssc = nsNSSCertificate::Create(serverCert);
-      CERT_DestroyCertificate(serverCert);
-      serverCert = nsnull;
-
-      nsCOMPtr<nsIX509Cert> prevcert;
-      infoObject->GetPreviousCert(getter_AddRefs(prevcert));
-
-      bool equals_previous = false;
-      if (prevcert && nssc) {
-        nsresult rv = nssc->Equals(prevcert, &equals_previous);
-        if (NS_FAILED(rv)) {
-          equals_previous = false;
-        }
-      }
-
-      if (equals_previous) {
-        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-               ("HandshakeCallback using PREV cert %p\n", prevcert.get()));
-        status->mServerCert = prevcert;
-      }
-      else {
-        if (status->mServerCert) {
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-                 ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get()));
-        }
-        else {
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-                 ("HandshakeCallback using NEW cert %p\n", nssc.get()));
-          status->mServerCert = nssc;
-        }
-      }
-    }
-
-    status->mHaveKeyLengthAndCipher = true;
-    status->mKeyLength = keyLength;
-    status->mSecretKeyLength = encryptBits;
-    status->mCipherName.Assign(cipherName);
-  }
-
-  PORT_Free(cipherName);
-  PR_FREEIF(certOrgName);
-  PR_Free(signer);
-}
+static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
 SECStatus
-PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool checksig, bool isServer)
+PSM_SSL_PKIX_AuthCertificate(CERTCertificate *peerCert, void * pinarg,
+                             const char * hostname,
+                             const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
     SECStatus          rv;
-    SECCertUsage       certUsage;
-    SECCertificateUsage certificateusage;
-    void *             pinarg;
-    char *             hostname;
     
-    pinarg = SSL_RevealPinArg(fd);
-    hostname = SSL_RevealURL(fd);
-    
-    /* this may seem backwards, but isn't. */
-    certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
-    certificateusage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
-
     if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
-        rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, checksig, certUsage,
-                                pinarg);
+        rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, true,
+                                certUsageSSLServer, pinarg);
     }
     else {
         nsresult nsrv;
         nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
         if (!inss)
           return SECFailure;
         nsRefPtr<nsCERTValInParamWrapper> survivingParams;
         if (NS_FAILED(inss->GetDefaultCERTValInParam(survivingParams)))
           return SECFailure;
 
         CERTValOutParam cvout[1];
         cvout[0].type = cert_po_end;
 
-        rv = CERT_PKIXVerifyCert(peerCert, certificateusage,
+        rv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
                                 survivingParams->GetRawPointerForNSS(),
                                 cvout, pinarg);
     }
 
-    if ( rv == SECSuccess && !isServer ) {
+    if (rv == SECSuccess) {
         /* cert is OK.  This is the client side of an SSL connection.
         * Now check the name field in the cert against the desired hostname.
         * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
         */
         if (hostname && hostname[0])
             rv = CERT_VerifyCertName(peerCert, hostname);
         else
             rv = SECFailure;
         if (rv != SECSuccess)
             PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
     }
         
-    PORT_Free(hostname);
     return rv;
 }
 
 struct nsSerialBinaryBlacklistEntry
 {
   unsigned int len;
   const char *binary_serial;
 };
@@ -1098,32 +360,98 @@ PSM_SSL_BlacklistDigiNotar(CERTCertifica
     // let's see if we want to worsen the error code to revoked.
     PRErrorCode revoked_code = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, serverCertChain);
     return (revoked_code != 0) ? revoked_code : SEC_ERROR_UNTRUSTED_ISSUER;
   }
 
   return 0;
 }
 
+// This function assumes that we will only use the SPDY connection coalescing
+// feature on connections where we have negotiated SPDY using NPN. If we ever
+// talk SPDY without having negotiated it with SPDY, this code will give wrong
+// and perhaps unsafe results.
+//
+// Returns SECSuccess on the initial handshake of all connections, on
+// renegotiations for any connections where we did not negotiate SPDY, or on any
+// SPDY connection where the server's certificate did not change.
+//
+// Prohibit changing the server cert only if we negotiated SPDY,
+// in order to support SPDY's cross-origin connection pooling.
 
-SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
-                                              PRBool checksig, PRBool isServer) {
-  nsNSSShutDownPreventionLock locker;
+static SECStatus
+BlockServerCertChangeForSpdy(nsNSSSocketInfo *infoObject,
+                             CERTCertificate *serverCert)
+{
+  // Get the existing cert. If there isn't one, then there is
+  // no cert change to worry about.
+  nsCOMPtr<nsIX509Cert> cert;
+  nsCOMPtr<nsIX509Cert2> cert2;
+
+  nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
+  if (!status) {
+    // If we didn't have a status, then this is the
+    // first handshake on this connection, not a
+    // renegotiation.
+    return SECSuccess;
+  }
+  
+  status->GetServerCert(getter_AddRefs(cert));
+  cert2 = do_QueryInterface(cert);
+  if (!cert2) {
+    NS_NOTREACHED("every nsSSLStatus must have a cert"
+                  "that implements nsIX509Cert2");
+    PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
+    return SECFailure;
+  }
 
-  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
-  CERTCertificateCleaner serverCertCleaner(serverCert);
+  // Filter out sockets that did not neogtiate SPDY via NPN
+  nsCAutoString negotiatedNPN;
+  nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
+  NS_ASSERTION(NS_SUCCEEDED(rv),
+               "GetNegotiatedNPN() failed during renegotiation");
+
+  if (NS_SUCCEEDED(rv) && !negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2")))
+    return SECSuccess;
+
+  // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
+  if (NS_FAILED(rv))
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+           ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
+            " Assuming spdy.\n"));
 
-  if (serverCert && 
-      serverCert->serialNumber.data &&
-      serverCert->issuerName &&
-      !strcmp(serverCert->issuerName, 
+  // Check to see if the cert has actually changed
+  CERTCertificate * c = cert2->GetCert();
+  NS_ASSERTION(c, "very bad and hopefully impossible state");
+  bool sameCert = CERT_CompareCerts(c, serverCert);
+  CERT_DestroyCertificate(c);
+  if (sameCert)
+    return SECSuccess;
+
+  // Report an error - changed cert is confirmed
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+         ("SPDY Refused to allow new cert during renegotiation\n"));
+  PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
+  return SECFailure;
+}
+
+SECStatus
+SSLServerCertVerificationJob::AuthCertificate(
+  nsNSSShutDownPreventionLock const & nssShutdownPreventionLock)
+{
+  if (BlockServerCertChangeForSpdy(mSocketInfo, mCert) != SECSuccess)
+    return SECFailure;
+
+  if (mCert->serialNumber.data &&
+      mCert->issuerName &&
+      !strcmp(mCert->issuerName, 
         "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
 
-    unsigned char *server_cert_comparison_start = (unsigned char*)serverCert->serialNumber.data;
-    unsigned int server_cert_comparison_len = serverCert->serialNumber.len;
+    unsigned char *server_cert_comparison_start = mCert->serialNumber.data;
+    unsigned int server_cert_comparison_len = mCert->serialNumber.len;
 
     while (server_cert_comparison_len) {
       if (*server_cert_comparison_start != 0)
         break;
 
       ++server_cert_comparison_start;
       --server_cert_comparison_len;
     }
@@ -1145,303 +473,281 @@ SECStatus PR_CALLBACK AuthCertificateCal
       if (server_cert_comparison_len == locked_cert_comparison_len &&
           !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
         PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
         return SECFailure;
       }
     }
   }
 
-  SECStatus rv = PSM_SSL_PKIX_AuthCertificate(fd, serverCert, checksig, isServer);
+  SECStatus rv = PSM_SSL_PKIX_AuthCertificate(mCert, mSocketInfo,
+                                              mSocketInfo->GetHostName(),
+                                              nssShutdownPreventionLock);
 
   // We want to remember the CA certs in the temp db, so that the application can find the
   // complete chain at any time it might need it.
   // But we keep only those CA certs in the temp db, that we didn't already know.
 
-  if (serverCert) {
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
-    nsRefPtr<nsNSSCertificate> nsc;
+  nsRefPtr<nsSSLStatus> status = mSocketInfo->SSLStatus();
+  nsRefPtr<nsNSSCertificate> nsc;
 
-    if (!status || !status->mServerCert) {
-      nsc = nsNSSCertificate::Create(serverCert);
-    }
+  if (!status || !status->mServerCert) {
+    nsc = nsNSSCertificate::Create(mCert);
+  }
 
-    CERTCertList *certList = nsnull;
-    certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
-    if (!certList) {
-      rv = SECFailure;
-    } else {
-      PRErrorCode blacklistErrorCode;
-      if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
-        blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(serverCert, certList);
-      } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
-        PRErrorCode savedErrorCode = PORT_GetError();
-        // Check if we want to worsen the error code to "revoked".
-        blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, certList);
-        if (blacklistErrorCode == 0) {
-          // we don't worsen the code, let's keep the original error code from NSS
-          PORT_SetError(savedErrorCode);
-        }
-      }
-      
-      if (blacklistErrorCode != 0) {
-        infoObject->SetCertIssuerBlacklisted();
-        PORT_SetError(blacklistErrorCode);
-        rv = SECFailure;
+  CERTCertList *certList = nsnull;
+  certList = CERT_GetCertChainFromCert(mCert, PR_Now(), certUsageSSLCA);
+  if (!certList) {
+    rv = SECFailure;
+  } else {
+    PRErrorCode blacklistErrorCode;
+    if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
+      blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(mCert, certList);
+    } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
+      PRErrorCode savedErrorCode = PORT_GetError();
+      // Check if we want to worsen the error code to "revoked".
+      blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(mCert, certList);
+      if (blacklistErrorCode == 0) {
+        // we don't worsen the code, let's keep the original error code from NSS
+        PORT_SetError(savedErrorCode);
       }
     }
-
-    if (rv == SECSuccess) {
-      if (nsc) {
-        bool dummyIsEV;
-        nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
-      }
-    
-      nsCOMPtr<nsINSSComponent> nssComponent;
       
-      for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
-           !CERT_LIST_END(node, certList);
-           node = CERT_LIST_NEXT(node)) {
-
-        if (node->cert->slot) {
-          // This cert was found on a token, no need to remember it in the temp db.
-          continue;
-        }
+    if (blacklistErrorCode != 0) {
+      mSocketInfo->SetCertIssuerBlacklisted();
+      PORT_SetError(blacklistErrorCode);
+      rv = SECFailure;
+    }
+  }
 
-        if (node->cert->isperm) {
-          // We don't need to remember certs already stored in perm db.
-          continue;
-        }
-        
-        if (node->cert == serverCert) {
-          // We don't want to remember the server cert, 
-          // the code that cares for displaying page info does this already.
-          continue;
-        }
+  if (rv == SECSuccess) {
+    if (nsc) {
+      bool dummyIsEV;
+      nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
+    }
+    
+    nsCOMPtr<nsINSSComponent> nssComponent;
+      
+    for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
+         !CERT_LIST_END(node, certList);
+         node = CERT_LIST_NEXT(node)) {
 
-        // We have found a signer cert that we want to remember.
-        char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
-        if (nickname && *nickname) {
-          PK11SlotInfo *slot = PK11_GetInternalKeySlot();
-          if (slot) {
-            PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
-                            nickname, false);
-            PK11_FreeSlot(slot);
-          }
-        }
-        PR_FREEIF(nickname);
+      if (node->cert->slot) {
+        // This cert was found on a token, no need to remember it in the temp db.
+        continue;
       }
 
+      if (node->cert->isperm) {
+        // We don't need to remember certs already stored in perm db.
+        continue;
+      }
+        
+      if (node->cert == mCert) {
+        // We don't want to remember the server cert, 
+        // the code that cares for displaying page info does this already.
+        continue;
+      }
+
+      // We have found a signer cert that we want to remember.
+      char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
+      if (nickname && *nickname) {
+        PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+        if (slot) {
+          PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
+                          nickname, false);
+          PK11_FreeSlot(slot);
+        }
+      }
+      PR_FREEIF(nickname);
     }
 
     if (certList) {
       CERT_DestroyCertList(certList);
     }
 
     // The connection may get terminated, for example, if the server requires
     // a client cert. Let's provide a minimal SSLStatus
     // to the caller that contains at least the cert and its status.
     if (!status) {
       status = new nsSSLStatus();
-      infoObject->SetSSLStatus(status);
+      mSocketInfo->SetSSLStatus(status);
     }
 
     if (rv == SECSuccess) {
       // Certificate verification succeeded delete any potential record
       // of certificate error bits.
       nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
-        infoObject, nsnull, rv);
+        mSocketInfo, nsnull, rv);
     }
     else {
       // Certificate verification failed, update the status' bits.
       nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
-        infoObject, status);
+        mSocketInfo, status);
     }
 
     if (status && !status->mServerCert) {
       status->mServerCert = nsc;
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
+             ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
     }
   }
 
   return rv;
 }
 
-struct OCSPDefaultResponders {
-    const char *issuerName_string;
-    CERTName *issuerName;
-    const char *issuerKeyID_base64;
-    SECItem *issuerKeyID;
-    const char *ocspUrl;
-};
+/*static*/ SECStatus
+SSLServerCertVerificationJob::Dispatch(const void * fdForLogging,
+                                       nsNSSSocketInfo * socketInfo,
+                                       CERTCertificate * serverCert)
+{
+  // Runs on the socket transport thread
+
+  if (!socketInfo || !serverCert) {
+    NS_ERROR("Invalid parameters for SSL server cert validation");
+    socketInfo->SetCertVerificationResult(PR_INVALID_STATE_ERROR,
+                                          PlainErrorMessage);
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
+    return SECFailure;
+  }
+  
+  nsRefPtr<SSLServerCertVerificationJob> job
+    = new SSLServerCertVerificationJob(fdForLogging, *socketInfo, *serverCert);
+
+  socketInfo->SetCertVerificationWaiting();
+  nsresult nrv;
+  if (!gCertVerificationThreadPool) {
+    nrv = NS_ERROR_NOT_INITIALIZED;
+  } else {
+    nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
+  }
+  if (NS_FAILED(nrv)) {
+    PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
+                      ? SEC_ERROR_NO_MEMORY
+                      : PR_INVALID_STATE_ERROR;
+    socketInfo->SetCertVerificationResult(error, PlainErrorMessage);
+    PORT_SetError(error);
+    return SECFailure;
+  }
+
+  PORT_SetError(PR_WOULD_BLOCK_ERROR);
+  return SECWouldBlock;    
+}
 
-static struct OCSPDefaultResponders myDefaultOCSPResponders[] = {
-  /* COMODO */
-  {
-    "CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE",
-    nsnull, "rb2YejS0Jvf6xCZU7wO94CTLVBo=", nsnull,
-    "http://ocsp.comodoca.com"
-  },
-  {
-    "CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
-    nsnull, "C1jli8ZMFTekQKkwqSG+RzZaVv8=", nsnull,
-    "http://ocsp.comodoca.com"
-  },
-  {
-    "CN=COMODO EV SGC CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
-    nsnull, "f/ZMNigUrs0eN6/eWvJbw6CsK/4=", nsnull,
-    "http://ocsp.comodoca.com"
-  },
-  {
-    "CN=COMODO EV SSL CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
-    nsnull, "aRZJ7LZ1ZFrpAyNgL1RipTRcPuI=", nsnull,
-    "http://ocsp.comodoca.com"
-  },
-  {
-    "CN=UTN - DATACorp SGC,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US",
-    nsnull, "UzLRs89/+uDxoF2FTpLSnkUdtE8=", nsnull,
-    "http://ocsp.usertrust.com"
-  },
-  {
-    "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US",
-    nsnull, "oXJfJhsomEOVXQc31YWWnUvSw0U=", nsnull,
-    "http://ocsp.usertrust.com"
-  },
-  /* Network Solutions */
-  {
-    "CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US",
-    nsnull, "ITDJ+wDXTpjah6oq0KcusUAxp0w=", nsnull,
-    "http://ocsp.netsolssl.com"
-  },
-  {
-    "CN=Network Solutions EV SSL CA,O=Network Solutions L.L.C.,C=US",
-    nsnull, "tk6FnYQfGx3UUolOB5Yt+d7xj8w=", nsnull,
-    "http://ocsp.netsolssl.com"
-  },
-  /* GlobalSign */
-  {
-    "CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE",
-    nsnull, "YHtmGkUNl8qJUC99BM00qP/8/Us=", nsnull,
-    "http://ocsp.globalsign.com/ExtendedSSLCACross"
-  },
-  {
-    "CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R2",
-    nsnull, "m+IHV2ccHsBqBt5ZtJot39wZhi4=", nsnull,
-    "http://ocsp.globalsign.com/ExtendedSSLCA"
-  },
-  {
-    "CN=GlobalSign Extended Validation CA,O=GlobalSign,OU=Extended Validation CA",
-    nsnull, "NLH5yYxrNUTMCGkK7uOjuVy/FuA=", nsnull,
-    "http://ocsp.globalsign.com/ExtendedSSL"
-  },
-  /* Trustwave */
-  {
-    "CN=SecureTrust CA,O=SecureTrust Corporation,C=US",
-    nsnull, "QjK2FvoE/f5dS3rD/fdMQB1aQ68=", nsnull,
-    "http://ocsp.trustwave.com"
-  }
-};
+NS_IMETHODIMP
+SSLServerCertVerificationJob::Run()
+{
+  // Runs on a cert verification thread
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+          ("[%p] SSLServerCertVerificationJob::Run\n", mSocketInfo.get()));
+
+  PRErrorCode error;
 
-static const unsigned int numResponders =
-    (sizeof myDefaultOCSPResponders) / (sizeof myDefaultOCSPResponders[0]);
-
-static CERT_StringFromCertFcn oldOCSPAIAInfoCallback = nsnull;
+  nsNSSShutDownPreventionLock nssShutdownPrevention;
+  if (mSocketInfo->isAlreadyShutDown()) {
+    error = SEC_ERROR_USER_CANCELLED;
+  } else {
+    // Reset the error code here so we can detect if AuthCertificate fails to
+    // set the error code if/when it fails.
+    PR_SetError(0, 0); 
+    SECStatus rv = AuthCertificate(nssShutdownPrevention);
+    if (rv == SECSuccess) {
+      nsRefPtr<SSLServerCertVerificationResult> restart 
+        = new SSLServerCertVerificationResult(*mSocketInfo, 0);
+      restart->Dispatch();
+      return NS_OK;
+    }
 
-/*
- * See if we have a hard-coded default responder for this certificate's
- * issuer (unless this certificate is a root certificate).
- *
- * The result needs to be freed (PORT_Free) when no longer in use.
- */
-char* PR_CALLBACK MyAlternateOCSPAIAInfoCallback(CERTCertificate *cert) {
-  if (cert && !cert->isRoot) {
-    unsigned int i;
-    for (i=0; i < numResponders; i++) {
-      if (!(myDefaultOCSPResponders[i].issuerName));
-      else if (!(myDefaultOCSPResponders[i].issuerKeyID));
-      else if (!(cert->authKeyID));
-      else if (CERT_CompareName(myDefaultOCSPResponders[i].issuerName,
-                                &(cert->issuer)) != SECEqual);
-      else if (SECITEM_CompareItem(myDefaultOCSPResponders[i].issuerKeyID,
-                                   &(cert->authKeyID->keyID)) != SECEqual);
-      else        // Issuer Name and Key Identifier match, so use this OCSP URL.
-        return PORT_Strdup(myDefaultOCSPResponders[i].ocspUrl);
+    error = PR_GetError();
+    if (error != 0) {
+      rv = HandleBadCertificate(error, mSocketInfo, *mCert, mFdForLogging,
+                                nssShutdownPrevention);
+      if (rv == SECSuccess) {
+        // The CertErrorRunnable will run on the main thread and it will dispatch
+        // the cert verification result to the socket transport thread, so we
+        // don't have to. This way, this verification thread doesn't need to
+        // wait for the CertErrorRunnable to complete.
+        return NS_OK; 
+      }
+      // DispatchCertErrorRunnable set a new error code.
+      error = PR_GetError(); 
     }
   }
 
-  // If we've not found a hard-coded default responder, chain to the old
-  // callback function (if there is one).
-  if (oldOCSPAIAInfoCallback)
-    return (*oldOCSPAIAInfoCallback)(cert);
-
-  return nsnull;
-}
-
-void cleanUpMyDefaultOCSPResponders() {
-  unsigned int i;
+  if (error == 0) {
+    NS_NOTREACHED("no error set during certificate validation failure");
+    error = PR_INVALID_STATE_ERROR;
+  }
 
-  for (i=0; i < numResponders; i++) {
-    if (myDefaultOCSPResponders[i].issuerName) {
-      CERT_DestroyName(myDefaultOCSPResponders[i].issuerName);
-      myDefaultOCSPResponders[i].issuerName = nsnull;
-    }
-    if (myDefaultOCSPResponders[i].issuerKeyID) {
-      SECITEM_FreeItem(myDefaultOCSPResponders[i].issuerKeyID, true);
-      myDefaultOCSPResponders[i].issuerKeyID = nsnull;
-    }
-  }
+  nsRefPtr<SSLServerCertVerificationResult> failure
+    = new SSLServerCertVerificationResult(*mSocketInfo, error);
+  failure->Dispatch();
+  return NS_OK;
 }
 
-SECStatus RegisterMyOCSPAIAInfoCallback() {
-  // Prevent multiple registrations.
-  if (myDefaultOCSPResponders[0].issuerName)
-    return SECSuccess;                 // Already registered ok.
+} // unnamed namespace
+
+// Extracts whatever information we need out of fd (using SSL_*) and passes it
+// to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
+// never do anything with fd except logging.
+SECStatus
+AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
+{
+  // Runs on the socket transport thread
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+         ("[%p] starting AuthCertificateHook\n", fd));
+
+  // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
+  // doing verification without checking signatures.
+  NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
 
-  // Populate various fields in the myDefaultOCSPResponders[] array.
-  SECStatus rv = SECFailure;
-  unsigned int i;
-  for (i=0; i < numResponders; i++) {
-    // Create a CERTName structure from the issuer name string.
-    myDefaultOCSPResponders[i].issuerName = CERT_AsciiToName(
-      const_cast<char*>(myDefaultOCSPResponders[i].issuerName_string));
-    if (!(myDefaultOCSPResponders[i].issuerName))
-      goto loser;
-    // Create a SECItem from the Base64 authority key identifier keyID.
-    myDefaultOCSPResponders[i].issuerKeyID = NSSBase64_DecodeBuffer(nsnull,
-          nsnull, myDefaultOCSPResponders[i].issuerKeyID_base64,
-          (PRUint32)PORT_Strlen(myDefaultOCSPResponders[i].issuerKeyID_base64));
-    if (!(myDefaultOCSPResponders[i].issuerKeyID))
-      goto loser;
+  // PSM never causes libssl to call this function with PR_TRUE for isServer,
+  // and many things in PSM assume that we are a client.
+  NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
+
+  if (!checkSig || isServer) {
+      PR_SetError(PR_INVALID_STATE_ERROR, 0);
+      return SECFailure;
   }
+      
+  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
 
-  // Register our alternate OCSP Responder URL lookup function.
-  rv = CERT_RegisterAlternateOCSPAIAInfoCallBack(MyAlternateOCSPAIAInfoCallback,
-                                                 &oldOCSPAIAInfoCallback);
-  if (rv != SECSuccess)
-    goto loser;
+  nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
+  SECStatus rv = SSLServerCertVerificationJob::Dispatch(
+                        static_cast<const void *>(fd), socketInfo, serverCert);
 
-  return SECSuccess;
+  CERT_DestroyCertificate(serverCert);
 
-loser:
-  cleanUpMyDefaultOCSPResponders();
   return rv;
 }
 
-SECStatus UnregisterMyOCSPAIAInfoCallback() {
-  SECStatus rv;
-
-  // Only allow unregistration if we're already registered.
-  if (!(myDefaultOCSPResponders[0].issuerName))
-    return SECFailure;
+SSLServerCertVerificationResult::SSLServerCertVerificationResult(
+        nsNSSSocketInfo & socketInfo, PRErrorCode errorCode,
+        SSLErrorMessageType errorMessageType)
+  : mSocketInfo(&socketInfo)
+  , mErrorCode(errorCode)
+  , mErrorMessageType(errorMessageType)
+{
+}
 
-  // Unregister our alternate OCSP Responder URL lookup function.
-  rv = CERT_RegisterAlternateOCSPAIAInfoCallBack(oldOCSPAIAInfoCallback,
-                                                 nsnull);
-  if (rv != SECSuccess)
-    return rv;
+void
+SSLServerCertVerificationResult::Dispatch()
+{
+  nsresult rv;
+  nsCOMPtr<nsIEventTarget> stsTarget
+    = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  NS_ASSERTION(stsTarget,
+               "Failed to get socket transport service event target");
+  rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+  NS_ASSERTION(NS_SUCCEEDED(rv), 
+               "Failed to dispatch SSLServerCertVerificationResult");
+}
 
-  // Tidy up.
-  oldOCSPAIAInfoCallback = nsnull;
-  cleanUpMyDefaultOCSPResponders();
-  return SECSuccess;
+NS_IMETHODIMP
+SSLServerCertVerificationResult::Run()
+{
+  // TODO: Assert that we're on the socket transport thread
+  mSocketInfo->SetCertVerificationResult(mErrorCode, mErrorMessageType);
+  return NS_OK;
 }
+
+} } // namespace mozilla::psm
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/src/SSLServerCertVerification.h
@@ -0,0 +1,89 @@
+/* -*- 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) 1998
+ * 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 ***** */
+#ifndef _SSLSERVERCERTVERIFICATION_H
+#define _SSLSERVERCERTVERIFICATION_H
+
+#include "seccomon.h"
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsIRunnable.h"
+#include "prerror.h"
+#include "nsNSSIOLayer.h"
+
+typedef struct PRFileDesc PRFileDesc;
+typedef struct CERTCertificateStr CERTCertificate;
+class nsNSSSocketInfo;
+class nsNSSShutDownPreventionLock;
+
+namespace mozilla { namespace psm {
+
+SECStatus AuthCertificateHook(void *arg, PRFileDesc *fd, 
+                              PRBool checkSig, PRBool isServer);
+
+SECStatus HandleBadCertificate(PRErrorCode defaultErrorCodeToReport,
+                               nsNSSSocketInfo * socketInfo,
+                               CERTCertificate & cert,
+                               const void * fdForLogging,
+                               const nsNSSShutDownPreventionLock &);
+
+// Dispatched from a cert verification thread to the STS thread to notify the
+// socketInfo of the verification result.
+//
+// This will cause the PR_Poll in the STS thread to return, so things work
+// correctly even if the STS thread is blocked polling (only) on the file
+// descriptor that is waiting for this result.
+class SSLServerCertVerificationResult : public nsRunnable
+{
+public:
+  NS_DECL_NSIRUNNABLE
+
+  SSLServerCertVerificationResult(nsNSSSocketInfo & socketInfo,
+                                  PRErrorCode errorCode,
+                                  SSLErrorMessageType errorMessageType = 
+                                      PlainErrorMessage);
+
+  void Dispatch();
+private:
+  const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
+  const PRErrorCode mErrorCode;
+  const SSLErrorMessageType mErrorMessageType;
+};
+
+} } // namespace mozilla::psm
+
+#endif
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/src/nsNSSCallbacks.cpp
@@ -35,58 +35,39 @@
  * 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 "nsNSSComponent.h" // for PIPNSS string bundle calls.
+#include "nsNSSComponent.h"
 #include "nsNSSCallbacks.h"
-#include "nsNSSCertificate.h"
-#include "nsNSSCleaner.h"
-#include "nsSSLStatus.h"
-#include "nsNSSIOLayer.h" // for nsNSSSocketInfo
+#include "nsNSSIOLayer.h"
 #include "nsIWebProgressListener.h"
-#include "nsIStringBundle.h"
-#include "nsXPIDLString.h"
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsIServiceManager.h"
-#include "nsReadableUtils.h"
-#include "nsIPrompt.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
 #include "nsProtectedAuthThread.h"
 #include "nsITokenDialogs.h"
-#include "nsCRT.h"
 #include "nsNSSShutDown.h"
 #include "nsIUploadChannel.h"
-#include "nsSSLThread.h"
 #include "nsThreadUtils.h"
-#include "nsIThread.h"
-#include "nsIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsProxyRelease.h"
 #include "PSMRunnable.h"
 #include "nsIConsoleService.h"
+#include "nsIHttpChannelInternal.h"
 
 #include "ssl.h"
-#include "cert.h"
 #include "ocsp.h"
 #include "nssb64.h"
-#include "secerr.h"
-#include "sslerr.h"
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
 
 class nsHTTPDownloadEvent : public nsRunnable {
 public:
   nsHTTPDownloadEvent();
@@ -148,16 +129,26 @@ nsHTTPDownloadEvent::Run()
     NS_ENSURE_STATE(uploadChannel);
 
     rv = uploadChannel->SetUploadStream(uploadStream, 
                                         mRequestSession->mPostContentType,
                                         -1);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Do not use SPDY for internal security operations. It could result
+  // in the silent upgrade to ssl, which in turn could require an SSL
+  // operation to fufill something like a CRL fetch, which is an
+  // endless loop.
+  nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(chan);
+  if (internalChannel) {
+    rv = internalChannel->SetAllowSpdy(false);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   nsCOMPtr<nsIHttpChannel> hchan = do_QueryInterface(chan);
   NS_ENSURE_STATE(hchan);
 
   rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mResponsibleForDoneSignal = false;
   mListener->mResponsibleForDoneSignal = true;
@@ -434,21 +425,20 @@ nsNSSHttpRequestSession::internal_send_r
 
       waitCondition.Wait(wait_interval);
       
       if (!waitFlag)
         break;
 
       if (!request_canceled)
       {
-        bool wantExit = nsSSLThread::stoppedOrStopping();
         bool timeout = 
           (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
-
-        if (wantExit || timeout)
+ 
+        if (timeout)
         {
           request_canceled = true;
 
           nsRefPtr<nsCancelHTTPDownloadEvent> cancelevent = new nsCancelHTTPDownloadEvent;
           cancelevent->mListener = mListener;
           rv = NS_DispatchToMainThread(cancelevent);
           if (NS_FAILED(rv)) {
             NS_WARNING("cannot post cancel event");
@@ -835,16 +825,22 @@ void PR_CALLBACK HandshakeCallback(PRFil
   nsNSSShutDownPreventionLock locker;
   PRInt32 sslStatus;
   char* signer = nsnull;
   char* cipherName = nsnull;
   PRInt32 keyLength;
   nsresult rv;
   PRInt32 encryptBits;
 
+  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
+
+  // If the handshake completed, then we know the site is TLS tolerant (if this
+  // was a TLS connection).
+  nsSSLIOLayerHelpers::rememberTolerantSite(fd, infoObject);
+
   if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
                                        &encryptBits, &signer, nsnull)) {
     return;
   }
 
   PRInt32 secStatus;
   if (sslStatus == SSL_SECURITY_STATUS_OFF)
     secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
@@ -856,17 +852,16 @@ void PR_CALLBACK HandshakeCallback(PRFil
                  nsIWebProgressListener::STATE_SECURE_LOW);
 
   PRBool siteSupportsSafeRenego;
   if (SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, &siteSupportsSafeRenego) != SECSuccess
       || !siteSupportsSafeRenego) {
 
     bool wantWarning = (nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() > 0);
 
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
     nsCOMPtr<nsIConsoleService> console;
     if (infoObject && wantWarning) {
       console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
       if (console) {
         nsXPIDLCString hostName;
         infoObject->GetHostName(getter_Copies(hostName));
 
         nsAutoString msg;
@@ -953,325 +948,38 @@ void PR_CALLBACK HandshakeCallback(PRFil
         }
       }
     }
 
     status->mHaveKeyLengthAndCipher = true;
     status->mKeyLength = keyLength;
     status->mSecretKeyLength = encryptBits;
     status->mCipherName.Assign(cipherName);
+
+    // Get the NPN value. Do this on the stack and copy it into
+    // a string rather than preallocating the string because right
+    // now we expect NPN to fail more often than it succeeds.
+    SSLNextProtoState state;
+    unsigned char npnbuf[256];
+    unsigned int npnlen;
+    
+    if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess &&
+        state == SSL_NEXT_PROTO_NEGOTIATED)
+      infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
+    else
+      infoObject->SetNegotiatedNPN(nsnull, 0);
+
+    infoObject->SetHandshakeCompleted();
   }
 
   PORT_Free(cipherName);
   PR_FREEIF(certOrgName);
   PR_Free(signer);
 }
 
-SECStatus
-PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool checksig, bool isServer)
-{
-    SECStatus          rv;
-    SECCertUsage       certUsage;
-    SECCertificateUsage certificateusage;
-    void *             pinarg;
-    char *             hostname;
-    
-    pinarg = SSL_RevealPinArg(fd);
-    hostname = SSL_RevealURL(fd);
-    
-    /* this may seem backwards, but isn't. */
-    certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
-    certificateusage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
-
-    if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
-        rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, checksig, certUsage,
-                                pinarg);
-    }
-    else {
-        nsresult nsrv;
-        nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
-        if (!inss)
-          return SECFailure;
-        nsRefPtr<nsCERTValInParamWrapper> survivingParams;
-        if (NS_FAILED(inss->GetDefaultCERTValInParam(survivingParams)))
-          return SECFailure;
-
-        CERTValOutParam cvout[1];
-        cvout[0].type = cert_po_end;
-
-        rv = CERT_PKIXVerifyCert(peerCert, certificateusage,
-                                survivingParams->GetRawPointerForNSS(),
-                                cvout, pinarg);
-    }
-
-    if ( rv == SECSuccess && !isServer ) {
-        /* cert is OK.  This is the client side of an SSL connection.
-        * Now check the name field in the cert against the desired hostname.
-        * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
-        */
-        if (hostname && hostname[0])
-            rv = CERT_VerifyCertName(peerCert, hostname);
-        else
-            rv = SECFailure;
-        if (rv != SECSuccess)
-            PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
-    }
-        
-    PORT_Free(hostname);
-    return rv;
-}
-
-struct nsSerialBinaryBlacklistEntry
-{
-  unsigned int len;
-  const char *binary_serial;
-};
-
-// bug 642395
-static struct nsSerialBinaryBlacklistEntry myUTNBlacklistEntries[] = {
-  { 17, "\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" },
-  { 17, "\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" },
-  { 16, "\x72\x03\x21\x05\xc5\x0c\x08\x57\x3d\x8e\xa5\x30\x4e\xfe\xe8\xb0" },
-  { 17, "\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" },
-  { 16, "\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" },
-  { 16, "\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" },
-  { 17, "\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" },
-  { 17, "\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" },
-  { 16, "\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" },
-  { 17, "\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" },
-  { 0, 0 } // end marker
-};
-
-// Call this if we have already decided that a cert should be treated as INVALID,
-// in order to check if we to worsen the error to REVOKED.
-PRErrorCode
-PSM_SSL_DigiNotarTreatAsRevoked(CERTCertificate * serverCert,
-                                CERTCertList * serverCertChain)
-{
-  // If any involved cert was issued by DigiNotar, 
-  // and serverCert was issued after 01-JUL-2011,
-  // then worsen the error to revoked.
-  
-  PRTime cutoff = 0;
-  PRStatus status = PR_ParseTimeString("01-JUL-2011 00:00", true, &cutoff);
-  if (status != PR_SUCCESS) {
-    NS_ASSERTION(status == PR_SUCCESS, "PR_ParseTimeString failed");
-    // be safe, assume it's afterwards, keep going
-  } else {
-    PRTime notBefore = 0, notAfter = 0;
-    if (CERT_GetCertTimes(serverCert, &notBefore, &notAfter) == SECSuccess &&
-           notBefore < cutoff) {
-      // no worsening for certs issued before the cutoff date
-      return 0;
-    }
-  }
-  
-  for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
-       !CERT_LIST_END(node, serverCertChain);
-       node = CERT_LIST_NEXT(node)) {
-    if (node->cert->issuerName &&
-        strstr(node->cert->issuerName, "CN=DigiNotar")) {
-      return SEC_ERROR_REVOKED_CERTIFICATE;
-    }
-  }
-  
-  return 0;
-}
-
-// Call this only if a cert has been reported by NSS as VALID
-PRErrorCode
-PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
-                           CERTCertList * serverCertChain)
-{
-  bool isDigiNotarIssuedCert = false;
-
-  for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
-       !CERT_LIST_END(node, serverCertChain);
-       node = CERT_LIST_NEXT(node)) {
-    if (!node->cert->issuerName)
-      continue;
-
-    if (strstr(node->cert->issuerName, "CN=DigiNotar")) {
-      isDigiNotarIssuedCert = true;
-    }
-  }
-
-  if (isDigiNotarIssuedCert) {
-    // let's see if we want to worsen the error code to revoked.
-    PRErrorCode revoked_code = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, serverCertChain);
-    return (revoked_code != 0) ? revoked_code : SEC_ERROR_UNTRUSTED_ISSUER;
-  }
-
-  return 0;
-}
-
-
-SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
-                                              PRBool checksig, PRBool isServer) {
-  nsNSSShutDownPreventionLock locker;
-
-  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
-  CERTCertificateCleaner serverCertCleaner(serverCert);
-
-  if (serverCert && 
-      serverCert->serialNumber.data &&
-      serverCert->issuerName &&
-      !strcmp(serverCert->issuerName, 
-        "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
-
-    unsigned char *server_cert_comparison_start = (unsigned char*)serverCert->serialNumber.data;
-    unsigned int server_cert_comparison_len = serverCert->serialNumber.len;
-
-    while (server_cert_comparison_len) {
-      if (*server_cert_comparison_start != 0)
-        break;
-
-      ++server_cert_comparison_start;
-      --server_cert_comparison_len;
-    }
-
-    nsSerialBinaryBlacklistEntry *walk = myUTNBlacklistEntries;
-    for ( ; walk && walk->len; ++walk) {
-
-      unsigned char *locked_cert_comparison_start = (unsigned char*)walk->binary_serial;
-      unsigned int locked_cert_comparison_len = walk->len;
-      
-      while (locked_cert_comparison_len) {
-        if (*locked_cert_comparison_start != 0)
-          break;
-        
-        ++locked_cert_comparison_start;
-        --locked_cert_comparison_len;
-      }
-
-      if (server_cert_comparison_len == locked_cert_comparison_len &&
-          !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
-        PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
-        return SECFailure;
-      }
-    }
-  }
-
-  SECStatus rv = PSM_SSL_PKIX_AuthCertificate(fd, serverCert, checksig, isServer);
-
-  // We want to remember the CA certs in the temp db, so that the application can find the
-  // complete chain at any time it might need it.
-  // But we keep only those CA certs in the temp db, that we didn't already know.
-
-  if (serverCert) {
-    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
-    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
-    nsRefPtr<nsNSSCertificate> nsc;
-
-    if (!status || !status->mServerCert) {
-      nsc = nsNSSCertificate::Create(serverCert);
-    }
-
-    CERTCertList *certList = nsnull;
-    certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
-    if (!certList) {
-      rv = SECFailure;
-    } else {
-      PRErrorCode blacklistErrorCode;
-      if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
-        blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(serverCert, certList);
-      } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
-        PRErrorCode savedErrorCode = PORT_GetError();
-        // Check if we want to worsen the error code to "revoked".
-        blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, certList);
-        if (blacklistErrorCode == 0) {
-          // we don't worsen the code, let's keep the original error code from NSS
-          PORT_SetError(savedErrorCode);
-        }
-      }
-      
-      if (blacklistErrorCode != 0) {
-        infoObject->SetCertIssuerBlacklisted();
-        PORT_SetError(blacklistErrorCode);
-        rv = SECFailure;
-      }
-    }
-
-    if (rv == SECSuccess) {
-      if (nsc) {
-        bool dummyIsEV;
-        nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
-      }
-    
-      nsCOMPtr<nsINSSComponent> nssComponent;
-      
-      for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
-           !CERT_LIST_END(node, certList);
-           node = CERT_LIST_NEXT(node)) {
-
-        if (node->cert->slot) {
-          // This cert was found on a token, no need to remember it in the temp db.
-          continue;
-        }
-
-        if (node->cert->isperm) {
-          // We don't need to remember certs already stored in perm db.
-          continue;
-        }
-        
-        if (node->cert == serverCert) {
-          // We don't want to remember the server cert, 
-          // the code that cares for displaying page info does this already.
-          continue;
-        }
-
-        // We have found a signer cert that we want to remember.
-        char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
-        if (nickname && *nickname) {
-          PK11SlotInfo *slot = PK11_GetInternalKeySlot();
-          if (slot) {
-            PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
-                            nickname, false);
-            PK11_FreeSlot(slot);
-          }
-        }
-        PR_FREEIF(nickname);
-      }
-
-    }
-
-    if (certList) {
-      CERT_DestroyCertList(certList);
-    }
-
-    // The connection may get terminated, for example, if the server requires
-    // a client cert. Let's provide a minimal SSLStatus
-    // to the caller that contains at least the cert and its status.
-    if (!status) {
-      status = new nsSSLStatus();
-      infoObject->SetSSLStatus(status);
-    }
-
-    if (rv == SECSuccess) {
-      // Certificate verification succeeded delete any potential record
-      // of certificate error bits.
-      nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
-        infoObject, nsnull, rv);
-    }
-    else {
-      // Certificate verification failed, update the status' bits.
-      nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
-        infoObject, status);
-    }
-
-    if (status && !status->mServerCert) {
-      status->mServerCert = nsc;
-      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-             ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
-    }
-  }
-
-  return rv;
-}
-
 struct OCSPDefaultResponders {
     const char *issuerName_string;
     CERTName *issuerName;
     const char *issuerKeyID_base64;
     SECItem *issuerKeyID;
     const char *ocspUrl;
 };
 
--- a/security/manager/ssl/src/nsNSSCallbacks.h
+++ b/security/manager/ssl/src/nsNSSCallbacks.h
@@ -47,21 +47,16 @@
 #include "nsIStreamLoader.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
 
 char* PR_CALLBACK
 PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
 
 void PR_CALLBACK HandshakeCallback(PRFileDesc *fd, void *client_data);
-SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
-                                              PRBool checksig, PRBool isServer);
-
-PRErrorCode PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
-                                       CERTCertList * serverCertChain);
 
 SECStatus RegisterMyOCSPAIAInfoCallback();
 SECStatus UnregisterMyOCSPAIAInfoCallback();
 
 class nsHTTPListener : public nsIStreamLoaderObserver
 {
 private:
   // For XPCOM implementations that are not a base class for some other
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -42,17 +42,16 @@
  * 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 "nsNSSComponent.h"
 #include "nsNSSCallbacks.h"
 #include "nsNSSIOLayer.h"
-#include "nsSSLThread.h"
 #include "nsCertVerificationThread.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryService.h"
 #include "nsIStreamListener.h"
 #include "nsIStringBundle.h"
 #include "nsIDirectoryService.h"
@@ -359,17 +358,17 @@ bool EnsureNSSInitialized(EnsureNSSOpera
   }
 }
 
 nsNSSComponent::nsNSSComponent()
   :mutex("nsNSSComponent.mutex"),
    mNSSInitialized(false),
    mCrlTimerLock("nsNSSComponent.mCrlTimerLock"),
    mThreadList(nsnull),
-   mSSLThread(NULL), mCertVerificationThread(NULL)
+   mCertVerificationThread(NULL)
 {
 #ifdef PR_LOGGING
   if (!gPIPNSSLog)
     gPIPNSSLog = PR_NewLogModule("pipnss");
 #endif
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ctor\n"));
   mUpdateTimerInitialized = false;
   crlDownloadTimerOn = false;
@@ -386,47 +385,32 @@ nsNSSComponent::nsNSSComponent()
   hashTableCerts = nsnull;
   mShutdownObjectList = nsNSSShutDownList::construct();
   mIsNetworkDown = false;
 }
 
 void 
 nsNSSComponent::deleteBackgroundThreads()
 {
-  if (mSSLThread)
-  {
-    mSSLThread->requestExit();
-    delete mSSLThread;
-    mSSLThread = nsnull;
-  }
   if (mCertVerificationThread)
   {
     mCertVerificationThread->requestExit();
     delete mCertVerificationThread;
     mCertVerificationThread = nsnull;
   }
 }
 
 void
 nsNSSComponent::createBackgroundThreads()
 {
-  NS_ASSERTION(mSSLThread == nsnull, "SSL thread already created.");
   NS_ASSERTION(mCertVerificationThread == nsnull,
                "Cert verification thread already created.");
 
-  mSSLThread = new nsSSLThread;
-  nsresult rv = mSSLThread->startThread();
-  if (NS_FAILED(rv)) {
-    delete mSSLThread;
-    mSSLThread = nsnull;
-    return;
-  }
-
   mCertVerificationThread = new nsCertVerificationThread;
-  rv = mCertVerificationThread->startThread();
+  nsresult rv = mCertVerificationThread->startThread();
   if (NS_FAILED(rv)) {
     delete mCertVerificationThread;
     mCertVerificationThread = nsnull;
   }
 }
 
 nsNSSComponent::~nsNSSComponent()
 {
@@ -1994,17 +1978,17 @@ nsNSSComponent::Init()
   mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
   nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
   
   mClientAuthRememberService = new nsClientAuthRememberService;
   if (mClientAuthRememberService)
     mClientAuthRememberService->Init();
 
   createBackgroundThreads();
-  if (!mSSLThread || !mCertVerificationThread)
+  if (!mCertVerificationThread)
   {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, could not create threads\n"));
 
     DeregisterObservers();
     mPIPNSSBundle = nsnull;
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
@@ -2544,18 +2528,16 @@ nsNSSComponent::DoProfileApproveChange(n
       status->VetoChange();
     }
   }
 }
 
 void
 nsNSSComponent::DoProfileChangeNetTeardown()
 {
-  if (mSSLThread)
-    mSSLThread->requestExit();
   if (mCertVerificationThread)
     mCertVerificationThread->requestExit();
   mIsNetworkDown = true;
 }
 
 void
 nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
 {
--- a/security/manager/ssl/src/nsNSSComponent.h
+++ b/security/manager/ssl/src/nsNSSComponent.h
@@ -232,17 +232,16 @@ private:
   ~nsCryptoHMAC();
   PK11Context* mHMACContext;
 
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
 };
 
 class nsNSSShutDownList;
-class nsSSLThread;
 class nsCertVerificationThread;
 
 // Implementation of the PSM component interface.
 class nsNSSComponent : public nsISignatureVerifier,
                        public nsIEntropyCollector,
                        public nsINSSComponent,
                        public nsIObserver,
                        public nsSupportsWeakReference,
@@ -352,17 +351,16 @@ private:
   bool mUpdateTimerInitialized;
   static int mInstanceCount;
   nsNSSShutDownList *mShutdownObjectList;
   SmartCardThreadList *mThreadList;
   bool mIsNetworkDown;
 
   void deleteBackgroundThreads();
   void createBackgroundThreads();
-  nsSSLThread *mSSLThread;
   nsCertVerificationThread *mCertVerificationThread;
 
   nsNSSHttpInterface mHttpForNSS;
   nsRefPtr<nsClientAuthRememberService> mClientAuthRememberService;
   nsRefPtr<nsCERTValInParamWrapper> mDefaultCERTValInParam;
   nsRefPtr<nsCERTValInParamWrapper> mDefaultCERTValInParamLocalOnly;
 
   static PRStatus PR_CALLBACK IdentityInfoInit(void);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -65,17 +65,17 @@
 #include "nsIStrictTransportSecurityService.h"
 
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsHashSets.h"
 #include "nsCRT.h"
 #include "nsAutoPtr.h"
 #include "nsPrintfCString.h"
-#include "nsSSLThread.h"
+#include "SSLServerCertVerification.h"
 #include "nsNSSShutDown.h"
 #include "nsSSLStatus.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCleaner.h"
 #include "nsThreadUtils.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsISecureBrowserUI.h"
@@ -105,18 +105,18 @@ using namespace mozilla::psm;
                             
 //#define DUMP_BUFFER  //Enable this define along with
                        //DEBUG_SSL_VERBOSE to dump SSL
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
+
 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
-NSSCleanupAutoPtrClass(char, PL_strfree)
 NSSCleanupAutoPtrClass(void, PR_FREEIF)
 NSSCleanupAutoPtrClass_WithParam(PRArenaPool, PORT_FreeArena, FalseParam, false)
 
 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
 
 /* SSM_UserCertChoice: enum for cert choice info */
 typedef enum {ASK, AUTO} SSM_UserCertChoice;
 
@@ -145,90 +145,45 @@ void MyLogFunction(const char *fmt, ...)
       return;
   PR_vfprintf(gMyLogFile, fmt, ap);
   va_end(ap);
 }
 
 #define PR_LOG(module,level,args) MyLogFunction args
 #endif
 
-
-nsSSLSocketThreadData::nsSSLSocketThreadData()
-: mSSLState(ssl_idle)
-, mPRErrorCode(PR_SUCCESS)
-, mSSLDataBuffer(nsnull)
-, mSSLDataBufferAllocatedSize(0)
-, mSSLRequestedTransferAmount(0)
-, mSSLRemainingReadResultData(nsnull)
-, mSSLResultRemainingBytes(0)
-, mReplacedSSLFileDesc(nsnull)
-, mOneBytePendingFromEarlierWrite(false)
-, mThePendingByte(0)
-, mOriginalRequestedTransferAmount(0)
-{
-}
-
-nsSSLSocketThreadData::~nsSSLSocketThreadData()
-{
-  NS_ASSERTION(mSSLState != ssl_pending_write
-               &&
-               mSSLState != ssl_pending_read, 
-               "oops??? ssl socket is not idle at the time it is being destroyed");
-  if (mSSLDataBuffer) {
-    nsMemory::Free(mSSLDataBuffer);
-  }
-}
-
-bool nsSSLSocketThreadData::ensure_buffer_size(PRInt32 amount)
-{
-  if (amount > mSSLDataBufferAllocatedSize) {
-    if (mSSLDataBuffer) {
-      mSSLDataBuffer = (char*)nsMemory::Realloc(mSSLDataBuffer, amount);
-    }
-    else {
-      mSSLDataBuffer = (char*)nsMemory::Alloc(amount);
-    }
-    
-    if (!mSSLDataBuffer)
-      return false;
-
-    mSSLDataBufferAllocatedSize = amount;
-  }
-  
-  return true;
-}
-
 nsNSSSocketInfo::nsNSSSocketInfo()
-  : mFd(nsnull),
-    mBlockingState(blocking_state_unknown),
+  : mMutex("nsNSSSocketInfo::nsNSSSocketInfo"),
+    mFd(nsnull),
+    mCertVerificationState(before_cert_verification),
     mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
     mSubRequestsHighSecurity(0),
     mSubRequestsLowSecurity(0),
     mSubRequestsBrokenSecurity(0),
     mSubRequestsNoSecurity(0),
-    mDocShellDependentStuffKnown(false),
-    mExternalErrorReporting(false),
+    mErrorCode(0),
+    mErrorMessageType(PlainErrorMessage),
     mForSTARTTLS(false),
     mHandshakePending(true),
-    mCanceled(false),
     mHasCleartextPhase(false),
     mHandshakeInProgress(false),
     mAllowTLSIntoleranceTimeout(true),
     mRememberClientAuthCertificate(false),
     mHandshakeStartTime(0),
     mPort(0),
-    mIsCertIssuerBlacklisted(false)
+    mIsCertIssuerBlacklisted(false),
+    mNPNCompleted(false),
+    mHandshakeCompleted(false),
+    mJoined(false),
+    mSentClientCert(false)
 {
-  mThreadData = new nsSSLSocketThreadData;
 }
 
 nsNSSSocketInfo::~nsNSSSocketInfo()
 {
-  delete mThreadData;
-
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return;
 
   shutdown(calledFromObject);
 }
 
 void nsNSSSocketInfo::virtualDestroyNSSReference()
@@ -282,24 +237,33 @@ nsNSSSocketInfo::SetPort(PRInt32 aPort)
 
 nsresult
 nsNSSSocketInfo::GetPort(PRInt32 *aPort)
 {
   *aPort = mPort;
   return NS_OK;
 }
 
-void nsNSSSocketInfo::SetCanceled(bool aCanceled)
+PRErrorCode
+nsNSSSocketInfo::GetErrorCode() const
 {
-  mCanceled = aCanceled;
+  MutexAutoLock lock(mMutex);
+
+  return mErrorCode;
 }
 
-bool nsNSSSocketInfo::GetCanceled()
+void
+nsNSSSocketInfo::SetCanceled(PRErrorCode errorCode,
+                             SSLErrorMessageType errorMessageType)
 {
-  return mCanceled;
+  MutexAutoLock lock(mMutex);
+
+  mErrorCode = errorCode;
+  mErrorMessageType = errorMessageType;
+  mErrorMessageCached.Truncate();
 }
 
 NS_IMETHODIMP nsNSSSocketInfo::GetRememberClientAuthCertificate(bool *aRememberClientAuthCertificate)
 {
   NS_ENSURE_ARG_POINTER(aRememberClientAuthCertificate);
   *aRememberClientAuthCertificate = mRememberClientAuthCertificate;
   return NS_OK;
 }
@@ -332,17 +296,16 @@ NS_IMETHODIMP
 nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
 {
   if (!aCallbacks) {
     mCallbacks = nsnull;
     return NS_OK;
   }
 
   mCallbacks = aCallbacks;
-  mDocShellDependentStuffKnown = false;
 
   return NS_OK;
 }
 
 static void
 getSecureBrowserUI(nsIInterfaceRequestor * callbacks,
                    nsISecureBrowserUI ** result)
 {
@@ -368,33 +331,16 @@ getSecureBrowserUI(nsIInterfaceRequestor
       
     nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(rootItem);
     if (docShell) {
       (void) docShell->GetSecurityUI(result);
     }
   }
 }
 
-// Are we running within a context that wants external SSL error reporting?
-// We'll look at the presence of a security UI object inside docshell.
-// If the docshell wants the lock icon, you'll get the ssl error pages, too.
-// This is helpful to distinguish from all other contexts, like mail windows,
-// or any other SSL connections running in the background.
-bool
-nsNSSSocketInfo::GetExternalErrorReporting()
-{
-  NS_ASSERTION(NS_IsMainThread(),
-               "nsNSSSocketInfo::GetExternalErrorReporting called off the "
-               "main thread.");
-
-  nsCOMPtr<nsISecureBrowserUI> secureUI;
-  getSecureBrowserUI(mCallbacks, getter_AddRefs(secureUI));
-  return secureUI != nsnull;
-}
-
 NS_IMETHODIMP
 nsNSSSocketInfo::GetSecurityState(PRUint32* state)
 {
   *state = mSecurityState;
   return NS_OK;
 }
 
 nsresult
@@ -469,29 +415,160 @@ nsNSSSocketInfo::GetShortSecurityDescrip
 
 nsresult
 nsNSSSocketInfo::SetShortSecurityDescription(const PRUnichar* aText) {
   mShortDesc.Assign(aText);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::GetErrorMessage(PRUnichar** aText) {
-  if (mErrorMessage.IsEmpty())
-    *aText = nsnull;
-  else {
-    *aText = ToNewUnicode(mErrorMessage);
-    NS_ENSURE_TRUE(*aText, NS_ERROR_OUT_OF_MEMORY);
+nsNSSSocketInfo::GetErrorMessage(PRUnichar** aText)
+{
+  NS_ENSURE_ARG_POINTER(aText);
+  *aText = nsnull;
+
+  if (!NS_IsMainThread()) {
+    NS_ERROR("nsNSSSocketInfo::GetErrorMessage called off the main thread");
+    return NS_ERROR_NOT_SAME_THREAD;
   }
-  return NS_OK;
+
+  MutexAutoLock lock(mMutex);
+
+  nsresult rv = formatErrorMessage(lock);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aText = ToNewUnicode(mErrorMessageCached);
+  return *aText != nsnull ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 void
-nsNSSSocketInfo::SetErrorMessage(const PRUnichar* aText) {
-  mErrorMessage.Assign(aText);
+nsNSSSocketInfo::SetNegotiatedNPN(const char *value, PRUint32 length)
+{
+  if (!value)
+    mNegotiatedNPN.Truncate();
+  else
+    mNegotiatedNPN.Assign(value, length);
+  mNPNCompleted = true;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetNegotiatedNPN(nsACString &aNegotiatedNPN)
+{
+  if (!mNPNCompleted)
+    return NS_ERROR_NOT_CONNECTED;
+
+  aNegotiatedNPN = mNegotiatedNPN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::JoinConnection(const nsACString & npnProtocol,
+                                const nsACString & hostname,
+                                PRInt32 port,
+                                bool *_retval NS_OUTPARAM)
+{
+  *_retval = false;
+
+  // Different ports may not be joined together
+  if (port != mPort)
+    return NS_OK;
+
+  // Make sure NPN has been completed and matches requested npnProtocol
+  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
+    return NS_OK;
+
+  // If this is the same hostname then the certicate status does not
+  // need to be considered. They are joinable.
+  if (mHostName && hostname.Equals(mHostName)) {
+    *_retval = true;
+    return NS_OK;
+  }
+
+  // Before checking the server certificate we need to make sure the
+  // handshake has completed.
+  if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->mServerCert)
+    return NS_OK;
+
+  // If the cert has error bits (e.g. it is untrusted) then do not join.
+  // The value of mHaveCertErrorBits is only reliable because we know that
+  // the handshake completed.
+  if (SSLStatus()->mHaveCertErrorBits)
+    return NS_OK;
+
+  // If the connection is using client certificates then do not join
+  // because the user decides on whether to send client certs to hosts on a
+  // per-domain basis.
+  if (mSentClientCert)
+    return NS_OK;
+
+  // Ensure that the server certificate covers the hostname that would
+  // like to join this connection
+
+  CERTCertificate *nssCert = nsnull;
+  CERTCertificateCleaner nsscertCleaner(nssCert);
+
+  nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(SSLStatus()->mServerCert);
+  if (cert2)
+    nssCert = cert2->GetCert();
+
+  if (!nssCert)
+    return NS_OK;
+
+  if (CERT_VerifyCertName(nssCert, PromiseFlatCString(hostname).get()) !=
+      SECSuccess)
+    return NS_OK;
+
+  // All tests pass - this is joinable
+  mJoined = true;
+  *_retval = true;
+  return NS_OK;
+}
+
+static nsresult
+formatPlainErrorMessage(nsXPIDLCString const & host, PRInt32 port,
+                        PRErrorCode err, nsString &returnedMessage);
+
+static nsresult
+formatOverridableCertErrorMessage(nsISSLStatus & sslStatus,
+                                  PRErrorCode errorCodeToReport, 
+                                  const nsXPIDLCString & host, PRInt32 port,
+                                  nsString & returnedMessage);
+
+// XXX: uses nsNSSComponent string bundles off the main thread when called by
+//      nsNSSSocketInfo::Write(). When we remove the error message from the
+//      serialization of nsNSSSocketInfo (bug 697781) we can inline
+//      formatErrorMessage into GetErrorMessage().
+nsresult
+nsNSSSocketInfo::formatErrorMessage(MutexAutoLock const & proofOfLock)
+{
+  if (mErrorCode == 0 || !mErrorMessageCached.IsEmpty()) {
+    return NS_OK;
+  }
+
+  nsresult rv;
+  NS_ConvertASCIItoUTF16 hostNameU(mHostName);
+  NS_ASSERTION(mErrorMessageType != OverridableCertErrorMessage || 
+                (mSSLStatus && mSSLStatus->mServerCert &&
+                 mSSLStatus->mHaveCertErrorBits),
+                "GetErrorMessage called for cert error without cert");
+  if (mErrorMessageType == OverridableCertErrorMessage && 
+      mSSLStatus && mSSLStatus->mServerCert) {
+    rv = formatOverridableCertErrorMessage(*mSSLStatus, mErrorCode,
+                                           mHostName, mPort,
+                                           mErrorMessageCached);
+  } else {
+    rv = formatPlainErrorMessage(mHostName, mPort, mErrorCode,
+                                 mErrorMessageCached);
+  }
+
+  if (NS_FAILED(rv)) {
+    mErrorMessageCached.Truncate();
+  }
+
+  return rv;
 }
 
 /* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
 NS_IMETHODIMP nsNSSSocketInfo::GetInterface(const nsIID & uuid, void * *result)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsNSSSocketInfo::GetInterface called off the main thread");
     return NS_ERROR_NOT_SAME_THREAD;
@@ -500,19 +577,16 @@ NS_IMETHODIMP nsNSSSocketInfo::GetInterf
   nsresult rv;
   if (!mCallbacks) {
     nsCOMPtr<nsIInterfaceRequestor> ir = new PipUIContext();
     if (!ir)
       return NS_ERROR_OUT_OF_MEMORY;
 
     rv = ir->GetInterface(uuid, result);
   } else {
-    if (nsSSLThread::stoppedOrStopping())
-      return NS_ERROR_FAILURE;
-
     rv = mCallbacks->GetInterface(uuid, result);
   }
   return rv;
 }
 
 nsresult
 nsNSSSocketInfo::GetForSTARTTLS(bool* aForSTARTTLS)
 {
@@ -534,25 +608,57 @@ nsNSSSocketInfo::ProxyStartSSL()
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::StartTLS()
 {
   return ActivateSSL();
 }
 
+NS_IMETHODIMP
+nsNSSSocketInfo::SetNPNList(nsTArray<nsCString> &protocolArray)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown())
+    return NS_ERROR_NOT_AVAILABLE;
+  if (!mFd)
+    return NS_ERROR_FAILURE;
+
+  // the npn list is a concatenated list of 8 bit byte strings.
+  nsCString npnList;
+
+  for (PRUint32 index = 0; index < protocolArray.Length(); ++index) {
+    if (protocolArray[index].IsEmpty() ||
+        protocolArray[index].Length() > 255)
+      return NS_ERROR_ILLEGAL_VALUE;
+
+    npnList.Append(protocolArray[index].Length());
+    npnList.Append(protocolArray[index]);
+  }
+  
+  if (SSL_SetNextProtoNego(
+        mFd,
+        reinterpret_cast<const unsigned char *>(npnList.get()),
+        npnList.Length()) != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  return NS_OK;
+}
+
 static NS_DEFINE_CID(kNSSCertificateCID, NS_X509CERT_CID);
 #define NSSSOCKETINFOMAGIC { 0xa9863a23, 0x26b8, 0x4a9c, \
   { 0x83, 0xf1, 0xe9, 0xda, 0xdb, 0x36, 0xb8, 0x30 } }
 static NS_DEFINE_CID(kNSSSocketInfoMagic, NSSSOCKETINFOMAGIC);
 
 NS_IMETHODIMP
 nsNSSSocketInfo::Write(nsIObjectOutputStream* stream) {
   stream->WriteID(kNSSSocketInfoMagic);
 
+  MutexAutoLock lock(mMutex);
+
   nsRefPtr<nsSSLStatus> status = mSSLStatus;
   nsCOMPtr<nsISerializable> certSerializable;
 
   // Write a redundant copy of the certificate for backward compatibility
   // with previous versions, which also unnecessarily wrote it.
   //
   // As we are reading the object our self, not using ReadObject, we have
   // to store it here 'manually' as well, mimicking our object stream
@@ -583,17 +689,21 @@ nsNSSSocketInfo::Write(nsIObjectOutputSt
   // to distinguish version number from mSecurityState
   // field stored in times before versioning has been introduced.
   // This mask value has been chosen as mSecurityState could
   // never be assigned such value.
   PRUint32 version = 3;
   stream->Write32(version | 0xFFFF0000);
   stream->Write32(mSecurityState);
   stream->WriteWStringZ(mShortDesc.get());
-  stream->WriteWStringZ(mErrorMessage.get());
+
+  // XXX: uses nsNSSComponent string bundles off the main thread
+  nsresult rv = formatErrorMessage(lock); 
+  NS_ENSURE_SUCCESS(rv, rv);
+  stream->WriteWStringZ(mErrorMessageCached.get());
 
   stream->WriteCompoundObject(NS_ISUPPORTS_CAST(nsISSLStatus*, status),
                               NS_GET_IID(nsISupports), true);
 
   stream->Write32((PRUint32)mSubRequestsHighSecurity);
   stream->Write32((PRUint32)mSubRequestsLowSecurity);
   stream->Write32((PRUint32)mSubRequestsBrokenSecurity);
   stream->Write32((PRUint32)mSubRequestsNoSecurity);
@@ -663,29 +773,32 @@ nsNSSSocketInfo::Read(nsIObjectInputStre
     // as we did before.
     stream->Read32(&version);
   }
   else {
     // There seems not to be the certificate present in the stream.
     version = UUID_0;
   }
 
+  MutexAutoLock lock(mMutex);
+
   // If the version field we have just read is not masked with 0xFFFF0000
   // then it is stored mSecurityState field and this is version 1 of
   // the binary data stream format.
   if ((version & 0xFFFF0000) == 0xFFFF0000) {
     version &= ~0xFFFF0000;
     stream->Read32(&mSecurityState);
   }
   else {
     mSecurityState = version;
     version = 1;
   }
   stream->ReadString(mShortDesc);
-  stream->ReadString(mErrorMessage);
+  stream->ReadString(mErrorMessageCached);
+  mErrorCode = 0;
 
   nsCOMPtr<nsISupports> obj;
   stream->ReadObject(true, getter_AddRefs(obj));
   
   mSSLStatus = reinterpret_cast<nsSSLStatus*>(obj.get());
 
   if (!mSSLStatus) {
     NS_WARNING("deserializing nsNSSSocketInfo without mSSLStatus");
@@ -768,20 +881,20 @@ nsNSSSocketInfo::GetClassIDNoAlloc(nsCID
 }
 
 nsresult nsNSSSocketInfo::ActivateSSL()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsresult rv = nsSSLThread::requestActivateSSL(this);
-  
-  if (NS_FAILED(rv))
-    return rv;
+  if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true))
+    return NS_ERROR_FAILURE;		
+  if (SECSuccess != SSL_ResetHandshake(mFd, false))
+    return NS_ERROR_FAILURE;
 
   mHandshakePending = true;
 
   return NS_OK;
 }
 
 nsresult nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr)
 {
@@ -833,16 +946,54 @@ void nsNSSSocketInfo::GetPreviousCert(ns
   }
 
   nsRefPtr<PreviousCertRunnable> runnable = new PreviousCertRunnable(mCallbacks);
   nsresult rv = runnable->DispatchToMainThreadAndWait();
   NS_ASSERTION(NS_SUCCEEDED(rv), "runnable->DispatchToMainThreadAndWait() failed");
   runnable->mPreviousCert.forget(_result);
 }
 
+void
+nsNSSSocketInfo::SetCertVerificationWaiting()
+{
+  // mCertVerificationState may be before_cert_verification for the first
+  // handshake on the connection, or after_cert_verification for subsequent
+  // renegotiation handshakes.
+  NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
+               "Invalid state transition to waiting_for_cert_verification");
+  mCertVerificationState = waiting_for_cert_verification;
+}
+
+void
+nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
+                                           SSLErrorMessageType errorMessageType)
+{
+  NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
+               "Invalid state transition to cert_verification_finished");
+
+  if (errorCode != 0) {
+    SetCanceled(errorCode, errorMessageType);
+  } else if (mFd) {
+    // We haven't closed the connection already, so restart it
+    SECStatus rv = SSL_RestartHandshakeAfterAuthCertificate(mFd);
+    if (rv != SECSuccess) {
+      errorCode = PR_GetError();
+      if (errorCode == 0) {
+        NS_ERROR("SSL_RestartHandshakeAfterAuthCertificate didn't set error code");
+        errorCode = PR_INVALID_STATE_ERROR;
+      }
+      SetCanceled(errorCode, PlainErrorMessage);
+    }
+  } else {
+    // If we closed the connection alreay, we don't have anything to do
+  }
+
+  mCertVerificationState = after_cert_verification;
+}
+
 nsresult nsNSSSocketInfo::GetSSLStatus(nsISSLStatus** _result)
 {
   NS_ENSURE_ARG_POINTER(_result);
 
   *_result = mSSLStatus;
   NS_IF_ADDREF(*_result);
 
   return NS_OK;
@@ -893,62 +1044,59 @@ void nsSSLIOLayerHelpers::Cleanup()
     mTLSTolerantSites = nsnull;
   }
 
   if (mRenegoUnrestrictedSites) {
     delete mRenegoUnrestrictedSites;
     mRenegoUnrestrictedSites = nsnull;
   }
 
-  if (mSharedPollableEvent)
-    PR_DestroyPollableEvent(mSharedPollableEvent);
-
   if (mutex) {
     delete mutex;
     mutex = nsnull;
   }
 
   if (mHostsWithCertErrors) {
     delete mHostsWithCertErrors;
     mHostsWithCertErrors = nsnull;
   }
 }
 
+/* Formats an error message for non-certificate-related SSL errors
+ * and non-overridable certificate errors (both are of type
+ * PlainErrormMessage). Use formatOverridableCertErrorMessage
+ * for overridable cert errors.
+ */
 static nsresult
-getErrorMessage(PRInt32 err, 
-                const nsString &host,
-                PRInt32 port,
-                nsINSSComponent *component,
-                nsString &returnedMessage)
+formatPlainErrorMessage(const nsXPIDLCString &host, PRInt32 port,
+                        PRErrorCode err, nsString &returnedMessage)
 {
-  NS_ENSURE_ARG_POINTER(component);
-
   const PRUnichar *params[1];
   nsresult rv;
 
+  nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (host.Length())
   {
     nsString hostWithPort;
 
     // For now, hide port when it's 443 and we're reporting the error.
     // In the future a better mechanism should be used
     // to make a decision about showing the port number, possibly by requiring
     // the context object to implement a specific interface.
     // The motivation is that Mozilla browser would like to hide the port number
     // in error pages in the common case.
 
-    if (port == 443) {
-      params[0] = host.get();
-    }
-    else {
-      hostWithPort = host;
+    hostWithPort.AssignASCII(host);
+    if (port != 443) {
       hostWithPort.AppendLiteral(":");
       hostWithPort.AppendInt(port);
-      params[0] = hostWithPort.get();
     }
+    params[0] = hostWithPort.get();
 
     nsString formattedString;
     rv = component->PIPBundleFormatStringFromName("SSLConnectionErrorPrefix", 
                                                   params, 1, 
                                                   formattedString);
     if (NS_SUCCEEDED(rv))
     {
       returnedMessage.Append(formattedString);
@@ -1297,187 +1445,188 @@ AppendErrorTextCode(PRErrorCode errorCod
     else {
       returnedMessage.Append(NS_LITERAL_STRING(" ("));
       returnedMessage.Append(idU);
       returnedMessage.Append(NS_LITERAL_STRING(")"));
     }
   }
 }
 
-static void
-getInvalidCertErrorMessage(PRUint32 multipleCollectedErrors, 
-                           PRErrorCode errorCodeToReport, 
-                           PRErrorCode errTrust, 
-                           PRErrorCode errMismatch, 
-                           PRErrorCode errExpired,
-                           const nsString &host,
-                           const nsString &hostWithPort,
-                           PRInt32 port,
-                           nsIX509Cert* ix509,
-                           nsString &returnedMessage)
+/* Formats an error message for overridable certificate errors (of type
+ * OverridableCertErrorMessage). Use formatPlainErrorMessage to format
+ * non-overridable cert errors and non-cert-related errors.
+ */
+static nsresult
+formatOverridableCertErrorMessage(nsISSLStatus & sslStatus,
+                                  PRErrorCode errorCodeToReport, 
+                                  const nsXPIDLCString & host, PRInt32 port,
+                                  nsString & returnedMessage)
 {
   const PRUnichar *params[1];
   nsresult rv;
+  nsAutoString hostWithPort;
+  nsAutoString hostWithoutPort;
 
   // For now, hide port when it's 443 and we're reporting the error.
   // In the future a better mechanism should be used
   // to make a decision about showing the port number, possibly by requiring
   // the context object to implement a specific interface.
   // The motivation is that Mozilla browser would like to hide the port number
   // in error pages in the common case.
   
-  if (port == 443)
-    params[0] = host.get();
-  else
+  hostWithoutPort.AppendASCII(host);
+  if (port == 443) {
+    params[0] = hostWithoutPort.get();
+  } else {
+    hostWithPort.AppendASCII(host);
+    hostWithPort.Append(':');
+    hostWithPort.AppendInt(port);
     params[0] = hostWithPort.get();
+  }
 
   nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
-  if (NS_FAILED(rv))
-    return;
-
-  nsString formattedString;
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  returnedMessage.Truncate();
   rv = component->PIPBundleFormatStringFromName("certErrorIntro", params, 1,
-                                                formattedString);
-  if (NS_SUCCEEDED(rv))
-  {
-    returnedMessage.Append(formattedString);
-    returnedMessage.Append(NS_LITERAL_STRING("\n\n"));
-  }
-
-  if (multipleCollectedErrors & nsICertOverrideService::ERROR_UNTRUSTED)
-  {
-    AppendErrorTextUntrusted(errTrust, host, ix509, 
+                                                returnedMessage);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  returnedMessage.Append(NS_LITERAL_STRING("\n\n"));
+
+  nsRefPtr<nsIX509Cert> ix509;
+  rv = sslStatus.GetServerCert(getter_AddRefs(ix509));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isUntrusted;
+  rv = sslStatus.GetIsUntrusted(&isUntrusted);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isUntrusted) {
+    AppendErrorTextUntrusted(errorCodeToReport, hostWithoutPort, ix509, 
                              component, returnedMessage);
   }
 
-  if (multipleCollectedErrors & nsICertOverrideService::ERROR_MISMATCH)
-  {
-    AppendErrorTextMismatch(host, ix509, component, returnedMessage);
+  bool isDomainMismatch;
+  rv = sslStatus.GetIsDomainMismatch(&isDomainMismatch);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isDomainMismatch) {
+    AppendErrorTextMismatch(hostWithoutPort, ix509, component, returnedMessage);
   }
 
-  if (multipleCollectedErrors & nsICertOverrideService::ERROR_TIME)
-  {
+  bool isNotValidAtThisTime;
+  rv = sslStatus.GetIsNotValidAtThisTime(&isNotValidAtThisTime);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isNotValidAtThisTime) {
     AppendErrorTextTime(ix509, component, returnedMessage);
   }
 
   AppendErrorTextCode(errorCodeToReport, component, returnedMessage);
+
+  return NS_OK;
 }
 
 static void
-nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRInt32 err)
+nsHandleSSLError(nsNSSSocketInfo *socketInfo, PRErrorCode err)
 {
   if (!NS_IsMainThread()) {
     NS_ERROR("nsHandleSSLError called off the main thread");
     return;
   }
 
-  // SetCanceled is only called by the main thread or the SSL thread. Whenever
-  // this function is called, the SSL thread is waiting on this thread (the
-  // main thread). So, no mutex is necessary for SetCanceled()/GetCanceled().
-  if (socketInfo->GetCanceled()) {
+  // SetCanceled is only called by the main thread or the socket transport
+  // thread. Whenever this function is called on the main thread, the SSL
+  // thread is blocked on it. So, no mutex is necessary for
+  // SetCanceled()/GetError*().
+  if (socketInfo->GetErrorCode()) {
     // If the socket has been flagged as canceled,
-    // the code who did was responsible for showing
-    // an error message (if desired).
-    return;
-  }
-
-  if (nsSSLThread::stoppedOrStopping()) {
+    // the code who did was responsible for setting the error code.
     return;
   }
 
   nsresult rv;
   NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
   if (NS_FAILED(rv))
     return;
 
   nsXPIDLCString hostName;
   socketInfo->GetHostName(getter_Copies(hostName));
 
   PRInt32 port;
   socketInfo->GetPort(&port);
 
-  bool suppressMessage = false;
-
   // Try to get a nsISSLErrorListener implementation from the socket consumer.
   nsCOMPtr<nsIInterfaceRequestor> cb;
   socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
   if (cb) {
     nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb);
     if (sel) {
       nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(socketInfo);
       nsCString hostWithPortString = hostName;
       hostWithPortString.AppendLiteral(":");
       hostWithPortString.AppendInt(port);
+    
+      bool suppressMessage = false; // obsolete, ignored
       rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
-      if (NS_SUCCEEDED(rv) && suppressMessage)
-        return;
     }
   }
 
-  if (socketInfo->GetExternalErrorReporting()) {
-    NS_ConvertASCIItoUTF16 hostNameU(hostName);
-    nsString formattedString;
-    (void) getErrorMessage(err, hostNameU, port, nssComponent, formattedString);
-    socketInfo->SetErrorMessage(formattedString.get());
+  socketInfo->SetCanceled(err, PlainErrorMessage);
+}
+
+namespace {
+
+nsNSSSocketInfo *
+getSocketInfoIfRunning(PRFileDesc * fd,
+                       const nsNSSShutDownPreventionLock & /*proofOfLock*/)
+{
+  if (!fd || !fd->lower || !fd->secret ||
+      fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) {
+    NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning");
+    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+    return nsnull;
   }
+
+  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
+
+  if (socketInfo->isAlreadyShutDown() || socketInfo->isPK11LoggedOut()) {
+    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
+    return nsnull;
+  }
+
+  if (socketInfo->GetErrorCode()) {
+    PRErrorCode err = socketInfo->GetErrorCode();
+    // If we get here, it is probably because cert verification failed and this
+    // is the first I/O attempt since that failure.
+    PR_SetError(err, 0);
+    return nsnull;
+  }
+
+  return socketInfo;
 }
 
+} // unnnamed namespace
+
 static PRStatus PR_CALLBACK
 nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
                     PRIntervalTime timeout)
 {
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] connecting SSL socket\n", (void*)fd));
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower)
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
   
-  PRStatus status = PR_SUCCESS;
-
-#if defined(XP_BEOS)
-  // Due to BeOS net_server's lack of support for nonblocking sockets,
-  // we must execute this entire connect as a blocking operation - bug 70217
- 
-  PRSocketOptionData sockopt;
-  sockopt.option = PR_SockOpt_Nonblocking;
-  PR_GetSocketOption(fd, &sockopt);
-  bool oldBlockVal = sockopt.value.non_blocking;
-  sockopt.option = PR_SockOpt_Nonblocking;
-  sockopt.value.non_blocking = false;
-  PR_SetSocketOption(fd, &sockopt);
-#endif
-  
-  status = fd->lower->methods->connect(fd->lower, addr, 
-#if defined(XP_BEOS)  // bug 70217
-                                       PR_INTERVAL_NO_TIMEOUT);
-#else
-                                       timeout);
-#endif
+  PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout);
   if (status != PR_SUCCESS) {
     PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("[%p] Lower layer connect error: %d\n",
                                       (void*)fd, PR_GetError()));
-#if defined(XP_BEOS)  // bug 70217
-    goto loser;
-#else
     return status;
-#endif
   }
-  
+
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Connect\n", (void*)fd));
-
-#if defined(XP_BEOS)  // bug 70217
- loser:
-  // We put the Nonblocking bit back to the value it was when 
-  // we entered this function.
-  NS_ASSERTION(sockopt.option == PR_SockOpt_Nonblocking,
-               "sockopt.option was re-set to an unexpected value");
-  sockopt.value.non_blocking = oldBlockVal;
-  PR_SetSocketOption(fd, &sockopt);
-#endif
-
   return status;
 }
 
 // nsPSMRememberCertErrorsTable
 
 nsPSMRememberCertErrorsTable::nsPSMRememberCertErrorsTable()
 {
   mErrorHosts.Init(16);
@@ -1633,28 +1782,34 @@ nsSSLIOLayerClose(PRFileDesc *fd)
   if (!fd)
     return PR_FAILURE;
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Shutting down socket\n", (void*)fd));
   
   nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
   NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
 
-  return nsSSLThread::requestClose(socketInfo);
+  return socketInfo->CloseSocketAndDestroy(locker);
 }
 
-PRStatus nsNSSSocketInfo::CloseSocketAndDestroy()
+PRStatus nsNSSSocketInfo::CloseSocketAndDestroy(
+  const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
-  nsNSSShutDownPreventionLock locker;
-
   nsNSSShutDownList::trackSSLSocketClose();
 
   PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
 
   PRStatus status = mFd->methods->close(mFd);
+  
+  // the nsNSSSocketInfo instance can out-live the connection, so we need some
+  // indication that the connection has been closed. mFd == nsnull is that
+  // indication. This is needed, for example, when the connection is closed
+  // before we have finished validating the server's certificate.
+  mFd = nsnull;
+  
   if (status != PR_SUCCESS) return status;
 
   popped->identity = PR_INVALID_IO_LAYER;
   NS_RELEASE_THIS();
   popped->dtor(popped);
 
   return PR_SUCCESS;
 }
@@ -1782,21 +1937,19 @@ class SSLErrorRunnable : public SyncRunn
   {
     nsHandleSSLError(mInfoObject, mErrorCode);
   }
   
   nsRefPtr<nsNSSSocketInfo> mInfoObject;
   const PRErrorCode mErrorCode;
 };
 
-PRInt32
-nsSSLThread::checkHandshake(PRInt32 bytesTransfered, 
-                            bool wasReading,
-                            PRFileDesc* ssl_layer_fd, 
-                            nsNSSSocketInfo *socketInfo)
+PRInt32 checkHandshake(PRInt32 bytesTransfered, bool wasReading,
+                       PRFileDesc* ssl_layer_fd,
+                       nsNSSSocketInfo *socketInfo)
 {
   // This is where we work around all of those SSL servers that don't 
   // conform to the SSL spec and shutdown a connection when we request
   // SSL v3.1 (aka TLS).  The spec says the client says what version
   // of the protocol we're willing to perform, in our case SSL v3.1
   // In its response, the server says which version it wants to perform.
   // Many servers out there only know how to do v3.0.  Next, we're supposed
   // to send back the version of the protocol we requested (ie v3.1).  At
@@ -1840,19 +1993,27 @@ nsSSLThread::checkHandshake(PRInt32 byte
 
       if (!wantRetry // no decision yet
           && isTLSIntoleranceError(err, socketInfo->GetHasCleartextPhase()))
       {
         wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo);
       }
     }
     
-    // This is the common place where we trigger an error message on a SSL socket.
-    // This might be reached at any time of the connection.
-    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err))) {
+    // This is the common place where we trigger non-cert-errors on a SSL
+    // socket. This might be reached at any time of the connection.
+    //
+    // The socketInfo->GetErrorCode() check is here to ensure we don't try to
+    // do the synchronous dispatch to the main thread unnecessarily after we've
+    // already handled a certificate error. (SSLErrorRunnable calls
+    // nsHandleSSLError, which has logic to avoid replacing the error message,
+    // so without the !socketInfo->GetErrorCode(), it would just be an
+    // expensive no-op.)
+    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
+        !socketInfo->GetErrorCode()) {
       nsRefPtr<SyncRunnableBase> runnable = new SSLErrorRunnable(socketInfo,
                                                                  err);
       (void) runnable->DispatchToMainThreadAndWait();
     }
   }
   else if (wasReading && 0 == bytesTransfered) // zero bytes on reading, socket closed
   {
     if (handleHandshakeResultNow)
@@ -1880,54 +2041,79 @@ nsSSLThread::checkHandshake(PRInt32 byte
     socketInfo->SetHandshakePending(false);
     socketInfo->SetHandshakeInProgress(false);
   }
   
   return bytesTransfered;
 }
 
 static PRInt16 PR_CALLBACK
-nsSSLIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
+nsSSLIOLayerPoll(PRFileDesc * fd, PRInt16 in_flags, PRInt16 *out_flags)
 {
-  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] polling SSL socket\n", (void*)fd));
   nsNSSShutDownPreventionLock locker;
 
   if (!out_flags)
   {
     NS_WARNING("nsSSLIOLayerPoll called with null out_flags");
     return 0;
   }
 
   *out_flags = 0;
 
-  if (!fd)
-  {
-    NS_WARNING("nsSSLIOLayerPoll called with null fd");
-    return 0;
+  nsNSSSocketInfo * socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo) {
+    // If we get here, it is probably because certificate validation failed
+    // and this is the first I/O operation after the failure. 
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+            ("[%p] polling SSL socket right after certificate verification failed "
+                  "or NSS shutdown or SDR logout %d\n",
+             fd, (int) in_flags));
+
+    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
+                 "caller did not poll for EXCEPT (canceled)");
+    // Since this poll method cannot return errors, we want the caller to call
+    // PR_Send/PR_Recv right away to get the error, so we tell that we are
+    // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning). 
+    *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619 
+    return in_flags;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestPoll(socketInfo, in_flags, out_flags);
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+         (socketInfo->IsWaitingForCertVerification()
+            ?  "[%p] polling SSL socket during certificate verification using lower %d\n"
+            :  "[%p] poll SSL socket using lower %d\n",
+         fd, (int) in_flags));
+
+  if (socketInfo->HandshakeTimeout()) {
+    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
+                 "caller did not poll for EXCEPT (handshake timeout)");
+    *out_flags = in_flags | PR_POLL_EXCEPT;
+    return in_flags;
+  }
+
+  // We want the handshake to continue during certificate validation, so we
+  // don't need to do anything special here. libssl automatically blocks when
+  // it reaches any point that would be unsafe to send/receive something before
+  // cert validation is complete.
+  PRInt16 result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
+                                    (void*)fd, (int) result));
+  return result;
 }
 
 bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
 PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
 PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
 Mutex *nsSSLIOLayerHelpers::mutex = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mTLSIntolerantSites = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mTLSTolerantSites = nsnull;
 nsPSMRememberCertErrorsTable *nsSSLIOLayerHelpers::mHostsWithCertErrors = nsnull;
 nsCStringHashSet *nsSSLIOLayerHelpers::mRenegoUnrestrictedSites = nsnull;
 bool nsSSLIOLayerHelpers::mTreatUnsafeNegotiationAsBroken = false;
 PRInt32 nsSSLIOLayerHelpers::mWarnLevelMissingRFC5746 = 1;
-PRFileDesc *nsSSLIOLayerHelpers::mSharedPollableEvent = nsnull;
-nsNSSSocketInfo *nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull;
-bool nsSSLIOLayerHelpers::mPollableEventCurrentlySet = false;
 
 static PRIntn _PSM_InvalidInt(void)
 {
     PR_ASSERT(!"I/O method is invalid");
     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
     return -1;
 }
 
@@ -1950,109 +2136,100 @@ static PRFileDesc *_PSM_InvalidDesc(void
     PR_ASSERT(!"I/O method is invalid");
     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
     return NULL;
 }
 
 static PRStatus PR_CALLBACK PSMGetsockname(PRFileDesc *fd, PRNetAddr *addr)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetsockname(socketInfo, addr);
+
+  return fd->lower->methods->getsockname(fd->lower, addr);
 }
 
 static PRStatus PR_CALLBACK PSMGetpeername(PRFileDesc *fd, PRNetAddr *addr)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetpeername(socketInfo, addr);
+
+  return fd->lower->methods->getpeername(fd->lower, addr);
 }
 
 static PRStatus PR_CALLBACK PSMGetsocketoption(PRFileDesc *fd, 
                                         PRSocketOptionData *data)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestGetsocketoption(socketInfo, data);
+
+  return fd->lower->methods->getsocketoption(fd, data);
 }
 
 static PRStatus PR_CALLBACK PSMSetsocketoption(PRFileDesc *fd, 
                                         const PRSocketOptionData *data)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker))
     return PR_FAILURE;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestSetsocketoption(socketInfo, data);
+
+  return fd->lower->methods->setsocketoption(fd, data);
 }
 
 static PRInt32 PR_CALLBACK PSMRecv(PRFileDesc *fd, void *buf, PRInt32 amount,
     PRIntn flags, PRIntervalTime timeout)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+  nsNSSSocketInfo *socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo)
     return -1;
-  }
-
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  if (flags == PR_MSG_PEEK) {
-    return nsSSLThread::requestRecvMsgPeek(socketInfo, buf, amount, flags, timeout);
-  }
-
-  if (flags != 0) {
+
+  if (flags != PR_MSG_PEEK && flags != 0) {
     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     return -1;
   }
 
-  return nsSSLThread::requestRead(socketInfo, buf, amount, timeout);
+  PRInt32 bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
+                                               timeout);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)fd, bytesRead));
+
+#ifdef DEBUG_SSL_VERBOSE
+  DEBUG_DUMP_BUFFER((unsigned char*)buf, bytesRead);
+#endif
+
+  return checkHandshake(bytesRead, true, fd, socketInfo);
 }
 
 static PRInt32 PR_CALLBACK PSMSend(PRFileDesc *fd, const void *buf, PRInt32 amount,
     PRIntn flags, PRIntervalTime timeout)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
+  nsNSSSocketInfo *socketInfo = getSocketInfoIfRunning(fd, locker);
+  if (!socketInfo)
     return -1;
-  }
 
   if (flags != 0) {
     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
     return -1;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestWrite(socketInfo, buf, amount, timeout);
+#ifdef DEBUG_SSL_VERBOSE
+  DEBUG_DUMP_BUFFER((unsigned char*)buf, amount);
+#endif
+
+  PRInt32 bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
+                                                  flags, timeout);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n",
+         fd, bytesWritten));
+
+  return checkHandshake(bytesWritten, false, fd, socketInfo);
 }
 
 static PRInt32 PR_CALLBACK
 nsSSLIOLayerRead(PRFileDesc* fd, void* buf, PRInt32 amount)
 {
   return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
 }
 
@@ -2060,24 +2237,21 @@ static PRInt32 PR_CALLBACK
 nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, PRInt32 amount)
 {
   return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
 }
 
 static PRStatus PR_CALLBACK PSMConnectcontinue(PRFileDesc *fd, PRInt16 out_flags)
 {
   nsNSSShutDownPreventionLock locker;
-  if (!fd || !fd->lower) {
+  if (!getSocketInfoIfRunning(fd, locker)) {
     return PR_FAILURE;
   }
 
-  nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret;
-  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
-
-  return nsSSLThread::requestConnectcontinue(socketInfo, out_flags);
+  return fd->lower->methods->connectcontinue(fd, out_flags);
 }
 
 nsresult nsSSLIOLayerHelpers::Init()
 {
   if (!nsSSLIOLayerInitialized) {
     nsSSLIOLayerInitialized = true;
     nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
     nsSSLIOLayerMethods  = *PR_GetDefaultIOMethods();
@@ -2112,20 +2286,16 @@ nsresult nsSSLIOLayerHelpers::Init()
     nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
     nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
     nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
     nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
   }
 
   mutex = new Mutex("nsSSLIOLayerHelpers.mutex");
 
-  mSharedPollableEvent = PR_NewPollableEvent();
-
-  // if we can not get a pollable event, we'll have to do busy waiting
-
   mTLSIntolerantSites = new nsCStringHashSet();
   if (!mTLSIntolerantSites)
     return NS_ERROR_OUT_OF_MEMORY;
 
   mTLSIntolerantSites->Init(1);
 
   mTLSTolerantSites = new nsCStringHashSet();
   if (!mTLSTolerantSites)
@@ -2717,32 +2887,32 @@ class ClientAuthDataRunnable : public Sy
 public:
   ClientAuthDataRunnable(CERTDistNames* caNames,
                          CERTCertificate** pRetCert,
                          SECKEYPrivateKey** pRetKey,
                          nsNSSSocketInfo * info,
                          CERTCertificate * serverCert) 
     : mRV(SECFailure)
     , mErrorCodeToReport(SEC_ERROR_NO_MEMORY)
-    , mCANames(caNames)
     , mPRetCert(pRetCert)
     , mPRetKey(pRetKey)
+    , mCANames(caNames)
     , mSocketInfo(info)
     , mServerCert(serverCert)
   {
   }
 
   SECStatus mRV;                        // out
   PRErrorCode mErrorCodeToReport;       // out
+  CERTCertificate** const mPRetCert;    // in/out
+  SECKEYPrivateKey** const mPRetKey;    // in/out
 protected:
   virtual void RunOnTargetThread();
 private:
   CERTDistNames* const mCANames;        // in
-  CERTCertificate** const mPRetCert;    // in/out
-  SECKEYPrivateKey** const mPRetKey;    // in/out
   nsNSSSocketInfo * const mSocketInfo;  // in
   CERTCertificate * const mServerCert;  // in
 };
 
 /*
  * Function: SECStatus SSM_SSLGetClientAuthData()
  * Purpose: this callback function is used to pull client certificate
  *			information upon server request
@@ -2775,27 +2945,42 @@ SECStatus nsNSS_SSLGetClientAuthData(voi
   CERTCertificate* serverCert = SSL_PeerCertificate(socket);
   if (!serverCert) {
     NS_NOTREACHED("Missing server certificate should have been detected during "
                   "server cert authentication.");
     PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
     return SECFailure;
   }
 
+  if (info->GetJoined()) {
+    // We refuse to send a client certificate when there are multiple hostnames
+    // joined on this connection, because we only show the user one hostname
+    // (mHostName) in the client certificate UI.
+
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+           ("[%p] Not returning client cert due to previous join\n", socket));
+    *pRetCert = nsnull;
+    *pRetKey = nsnull;
+    return SECSuccess;
+  }
+
   // XXX: This should be done asynchronously; see bug 696976
   nsRefPtr<ClientAuthDataRunnable> runnable =
     new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert);
   nsresult rv = runnable->DispatchToMainThreadAndWait();
   if (NS_FAILED(rv)) {
     PR_SetError(SEC_ERROR_NO_MEMORY, 0);
     return SECFailure;
   }
   
   if (runnable->mRV != SECSuccess) {
-    PORT_SetError(runnable->mErrorCodeToReport);
+    PR_SetError(runnable->mErrorCodeToReport, 0);
+  } else if (*runnable->mPRetCert || *runnable->mPRetKey) {
+    // Make joinConnection prohibit joining after we've sent a client cert
+    info->SetSentClientCert();
   }
 
   return runnable->mRV;
 }
 
 void ClientAuthDataRunnable::RunOnTargetThread()
 {
   PRArenaPool* arena = NULL;
@@ -3215,390 +3400,408 @@ done:
 }
 
 class CertErrorRunnable : public SyncRunnableBase
 {
  public:
   CertErrorRunnable(const void * fdForLogging,
                     nsIX509Cert * cert,
                     nsNSSSocketInfo * infoObject,
-                    const CERTVerifyLog * verify_log,
-                    bool hasCertNameMismatch,
-                    PRErrorCode defaultErrorCodeToReport)
+                    PRErrorCode defaultErrorCodeToReport,
+                    PRUint32 collectedErrors,
+                    PRErrorCode errorCodeTrust,
+                    PRErrorCode errorCodeMismatch,
+                    PRErrorCode errorCodeExpired)
     : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
-      mVerifyLog(verify_log), mHasCertNameMismatch(hasCertNameMismatch),
-      mRv(SECFailure), mErrorCodeToReport(defaultErrorCodeToReport)
+      mDefaultErrorCodeToReport(defaultErrorCodeToReport),
+      mCollectedErrors(collectedErrors),
+      mErrorCodeTrust(errorCodeTrust),
+      mErrorCodeMismatch(errorCodeMismatch),
+      mErrorCodeExpired(errorCodeExpired)
   {
   }
 
+  NS_DECL_NSIRUNNABLE
   virtual void RunOnTargetThread();
+  nsCOMPtr<nsIRunnable> mResult; // out
+private:
+  SSLServerCertVerificationResult* CheckCertOverrides();
   
-  // in
-  const void * const mFdForLogging;
-  nsCOMPtr<nsIX509Cert> mCert;
-  nsNSSSocketInfo * const mInfoObject;
-  const CERTVerifyLog * const mVerifyLog;
-  const bool mHasCertNameMismatch;
-  nsXPIDLCString mHostname;
-
-  SECStatus mRv; // out
-  PRErrorCode mErrorCodeToReport; // in/out
+  const void * const mFdForLogging; // may become an invalid pointer; do not dereference
+  const nsCOMPtr<nsIX509Cert> mCert;
+  const nsRefPtr<nsNSSSocketInfo> mInfoObject;
+  const PRErrorCode mDefaultErrorCodeToReport;
+  const PRUint32 mCollectedErrors;
+  const PRErrorCode mErrorCodeTrust;
+  const PRErrorCode mErrorCodeMismatch;
+  const PRErrorCode mErrorCodeExpired;
 };
 
-static SECStatus
-cancel_and_failure(nsNSSSocketInfo* infoObject)
-{
-  infoObject->SetCanceled(true);
-  return SECFailure;
-}
-
-static SECStatus
-nsNSSBadCertHandler(void *arg, PRFileDesc *sslSocket)
+namespace mozilla { namespace psm {
+
+// Returns SECSuccess if it dispatched the CertErrorRunnable. In that case,
+// the caller should NOT dispatch its own SSLServerCertVerificationResult;
+// the CertErrorRunnable will do it instead.
+//
+// Returns SECFailure with the error code set if it does not dispatch the
+// CertErrorRunnable. In that case, the caller should dispatch its own 
+// SSLServerCertVerificationResult with the error code from PR_GetError().
+SECStatus
+HandleBadCertificate(PRErrorCode defaultErrorCodeToReport,
+                    nsNSSSocketInfo * socketInfo, CERTCertificate & cert,
+                    const void * fdForLogging,
+                    const nsNSSShutDownPreventionLock & /*proofOfLock*/)
 {
   // cert was revoked, don't do anything else
-  // Calling cancel_and_failure is not necessary, and would be wrong,
-  // [for errors other than the ones explicitly handled below,] 
-  // because it suppresses error reporting.
-  PRErrorCode defaultErrorCodeToReport = PR_GetError();
-  if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE)
+  if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE) {
+    PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
     return SECFailure;
+  }
 
   if (defaultErrorCodeToReport == 0) {
     NS_ERROR("No error code set during certificate validation failure.");
-    defaultErrorCodeToReport = SEC_ERROR_CERT_NOT_VALID;
-  }
-
-  nsNSSShutDownPreventionLock locker;
-  nsNSSSocketInfo* infoObject = (nsNSSSocketInfo *)arg;
-  if (!infoObject)
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
     return SECFailure;
-
-  if (nsSSLThread::stoppedOrStopping())
-    return cancel_and_failure(infoObject);
-
-  CERTCertificate *peerCert = nsnull;
-  CERTCertificateCleaner peerCertCleaner(peerCert);
-  peerCert = SSL_PeerCertificate(sslSocket);
-  if (!peerCert)
-    return cancel_and_failure(infoObject);
+  }
 
   nsRefPtr<nsNSSCertificate> nssCert;
-  nssCert = nsNSSCertificate::Create(peerCert);
-  if (!nssCert)
-    return cancel_and_failure(infoObject);
+  nssCert = nsNSSCertificate::Create(&cert);
+  if (!nssCert) {
+    NS_ERROR("nsNSSCertificate::Create failed in DispatchCertErrorRunnable");
+    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+    return SECFailure;
+  }
 
   SECStatus srv;
   nsresult nsrv;
 
   nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
-  if (!inss)
-    return cancel_and_failure(infoObject);
+  if (!inss) {
+    NS_ERROR("do_GetService(kNSSComponentCID) failed in DispatchCertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
+
   nsRefPtr<nsCERTValInParamWrapper> survivingParams;
   nsrv = inss->GetDefaultCERTValInParam(survivingParams);
-  if (NS_FAILED(nsrv))
-    return cancel_and_failure(infoObject);
+  if (NS_FAILED(nsrv)) {
+    NS_ERROR("GetDefaultCERTValInParam failed in DispatchCertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
   
-  char *hostname = SSL_RevealURL(sslSocket);
-  if (!hostname)
-    return cancel_and_failure(infoObject);
-
-  charCleaner hostnameCleaner(hostname); 
+  PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+  PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
+  if (!log_arena) {
+    NS_ERROR("PORT_NewArena failed in DispatchCertErrorRunnable");
+    return SECFailure; // PORT_NewArena set error code
+  }
+
+  CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
+  if (!verify_log) {
+    NS_ERROR("PORT_ArenaZNew failed in DispatchCertErrorRunnable");
+    return SECFailure; // PORT_ArenaZNew set error code
+  }
+  CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
+  verify_log->arena = log_arena;
+
+  if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
+    srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), &cert,
+                                true, certificateUsageSSLServer,
+                                PR_Now(), static_cast<void*>(socketInfo),
+                                verify_log, NULL);
+  }
+  else {
+    CERTValOutParam cvout[2];
+    cvout[0].type = cert_po_errorLog;
+    cvout[0].value.pointer.log = verify_log;
+    cvout[1].type = cert_po_end;
+
+    srv = CERT_PKIXVerifyCert(&cert, certificateUsageSSLServer,
+                              survivingParams->GetRawPointerForNSS(),
+                              cvout, static_cast<void*>(socketInfo));
+  }
+
+  // We ignore the result code of the cert verification.
+  // Either it is a failure, which is expected, and we'll process the
+  //                         verify log below.
+  // Or it is a success, then a domain mismatch is the only 
+  //                     possible failure. 
+
+  PRErrorCode errorCodeMismatch = 0;
+  PRErrorCode errorCodeTrust = 0;
+  PRErrorCode errorCodeExpired = 0;
+
+  PRUint32 collected_errors = 0;
+
+  if (socketInfo->IsCertIssuerBlacklisted()) {
+    collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+    errorCodeTrust = defaultErrorCodeToReport;
+  }
 
   // Check the name field against the desired hostname.
-  bool hasCertNameMismatch =
-      hostname[0] && CERT_VerifyCertName(peerCert, hostname) != SECSuccess;
-
+  if (CERT_VerifyCertName(&cert, socketInfo->GetHostName()) != SECSuccess) {
+    collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+    errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
+  }
+
+  CERTVerifyLogNode *i_node;
+  for (i_node = verify_log->head; i_node; i_node = i_node->next)
   {
-    PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if (!log_arena)    
-      return cancel_and_failure(infoObject);
-
-    PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
-
-    CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
-    if (!verify_log)
-      return cancel_and_failure(infoObject);
-
-    CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
-
-    verify_log->arena = log_arena;
-
-    if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
-      srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), peerCert,
-                                  true, certificateUsageSSLServer,
-                                  PR_Now(), (void*)infoObject, 
-                                  verify_log, NULL);
-    }
-    else {
-      CERTValOutParam cvout[2];
-      cvout[0].type = cert_po_errorLog;
-      cvout[0].value.pointer.log = verify_log;
-      cvout[1].type = cert_po_end;
-
-      srv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
-                                survivingParams->GetRawPointerForNSS(),
-                                cvout, (void*)infoObject);
+    switch (i_node->error)
+    {
+      case SEC_ERROR_UNKNOWN_ISSUER:
+      case SEC_ERROR_CA_CERT_INVALID:
+      case SEC_ERROR_UNTRUSTED_ISSUER:
+      case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+      case SEC_ERROR_UNTRUSTED_CERT:
+      case SEC_ERROR_INADEQUATE_KEY_USAGE:
+        // We group all these errors as "cert not trusted"
+        collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
+        if (errorCodeTrust == SECSuccess) {
+          errorCodeTrust = i_node->error;
+        }
+        break;
+      case SSL_ERROR_BAD_CERT_DOMAIN:
+        collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
+        if (errorCodeMismatch == SECSuccess) {
+          errorCodeMismatch = i_node->error;
+        }
+        break;
+      case SEC_ERROR_EXPIRED_CERTIFICATE:
+        collected_errors |= nsICertOverrideService::ERROR_TIME;
+        if (errorCodeExpired == SECSuccess) {
+          errorCodeExpired = i_node->error;
+        }
+        break;
+      default:
+        PR_SetError(i_node->error, 0);
+        return SECFailure;
     }
-
-    // We ignore the result code of the cert verification.
-    // Either it is a failure, which is expected, and we'll process the
-    //                         verify log below.
-    // Or it is a success, then a domain mismatch is the only 
-    //                     possible failure. 
-
-    nsRefPtr<CertErrorRunnable> runnable =
-      new CertErrorRunnable(static_cast<void*>(sslSocket), 
-                            static_cast<nsIX509Cert*>(nssCert.get()),
-                            infoObject, verify_log, hasCertNameMismatch,
-                            defaultErrorCodeToReport);
-
-    // now grab the host name to pass to the STS Service
-    nsrv = infoObject->GetHostName(getter_Copies(runnable->mHostname));
-    if (NS_FAILED(nsrv)) {
-      PR_SetError(defaultErrorCodeToReport, 0);
-      return SECFailure;
-    }
-
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-           ("[%p][%p] Before dispatching CertErrorRunnable\n",
-           sslSocket, runnable.get()));
-
-    // Dispatch SYNC since the result is used below
-    (void) runnable->DispatchToMainThreadAndWait();
-
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
-           ("[%p][%p] After dispatching CertErrorRunnable\n",
-           sslSocket, runnable.get()));
-
-    if (runnable->mRv == SECSuccess)
-      return SECSuccess;
-  
-    NS_ASSERTION(runnable->mErrorCodeToReport != 0,
-                 "CertErrorRunnable did not set error code.");
-    PR_SetError(runnable->mErrorCodeToReport ? runnable->mErrorCodeToReport
-                                             : defaultErrorCodeToReport, 0);
+  }
+
+  if (!collected_errors)
+  {
+    // This will happen when CERT_*Verify* only returned error(s) that are
+    // not on our whitelist of overridable certificate errors.
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] !collected_errors: %d\n",
+           fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
+    PR_SetError(defaultErrorCodeToReport, 0);
     return SECFailure;
   }
+
+  socketInfo->SetStatusErrorBits(*nssCert, collected_errors);
+
+  nsRefPtr<CertErrorRunnable> runnable =
+    new CertErrorRunnable(fdForLogging, 
+                          static_cast<nsIX509Cert*>(nssCert.get()),
+                          socketInfo, defaultErrorCodeToReport, 
+                          collected_errors, errorCodeTrust, 
+                          errorCodeMismatch, errorCodeExpired);
+
+  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+          ("[%p][%p] Before dispatching CertErrorRunnable\n",
+          fdForLogging, runnable.get()));
+
+  nsresult nrv;
+  nsCOMPtr<nsIEventTarget> stsTarget
+    = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
+  if (NS_SUCCEEDED(nrv)) {
+    nrv = stsTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  }
+  if (NS_FAILED(nrv)) {
+    NS_ERROR("Failed to dispatch CertErrorRunnable");
+    PR_SetError(defaultErrorCodeToReport, 0);
+    return SECFailure;
+  }
+
+  return SECSuccess;
 }
 
-void CertErrorRunnable::RunOnTargetThread()
+} } // namespace mozilla::psm
+
+void
+nsNSSSocketInfo::SetStatusErrorBits(nsIX509Cert & cert,
+                                    PRUint32 collected_errors)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (!mSSLStatus)
+    mSSLStatus = new nsSSLStatus();
+
+  mSSLStatus->mServerCert = &cert;
+
+  mSSLStatus->mHaveCertErrorBits = true;
+  mSSLStatus->mIsDomainMismatch = 
+    collected_errors & nsICertOverrideService::ERROR_MISMATCH;
+  mSSLStatus->mIsNotValidAtThisTime = 
+    collected_errors & nsICertOverrideService::ERROR_TIME;
+  mSSLStatus->mIsUntrusted = 
+    collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
+
+  nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
+    this, mSSLStatus, SECFailure);
+}
+
+SSLServerCertVerificationResult *
+CertErrorRunnable::CheckCertOverrides()
 {
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CertErrorRunnable::Run\n",
                                     mFdForLogging, this));
 
   if (!NS_IsMainThread()) {
-    NS_ERROR("CertErrorRunnable::RunOnTargetThread called off main thread");
-    return;
-  }
-
-  if (nsSSLThread::stoppedOrStopping())
-    return;
- 
-  PRErrorCode errorCodeMismatch = 0;
-  PRErrorCode errorCodeTrust = 0;
-  PRErrorCode errorCodeExpired = 0;
-
-  PRUint32 collected_errors = 0;
-
-  if (mInfoObject->IsCertIssuerBlacklisted()) {
-    collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
-    errorCodeTrust = mErrorCodeToReport;
-  }
-
-  if (mHasCertNameMismatch) {
-    collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
-    errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
+    NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
+    return new SSLServerCertVerificationResult(*mInfoObject,
+                                               mDefaultErrorCodeToReport);
   }
 
-  {
-    CERTVerifyLogNode *i_node;
-    for (i_node = mVerifyLog->head; i_node; i_node = i_node->next)
-    {
-      switch (i_node->error)
-      {
-        case SEC_ERROR_UNKNOWN_ISSUER:
-        case SEC_ERROR_CA_CERT_INVALID:
-        case SEC_ERROR_UNTRUSTED_ISSUER:
-        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-        case SEC_ERROR_UNTRUSTED_CERT:
-        case SEC_ERROR_INADEQUATE_KEY_USAGE:
-          // We group all these errors as "cert not trusted"
-          collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
-          if (errorCodeTrust == SECSuccess) {
-            errorCodeTrust = i_node->error;
-          }
-          break;
-        case SSL_ERROR_BAD_CERT_DOMAIN:
-          collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
-          if (errorCodeMismatch == SECSuccess) {
-            errorCodeMismatch = i_node->error;
-          }
-          break;
-        case SEC_ERROR_EXPIRED_CERTIFICATE:
-          collected_errors |= nsICertOverrideService::ERROR_TIME;
-          if (errorCodeExpired == SECSuccess) {
-            errorCodeExpired = i_node->error;
-          }
-          break;
-        default:
-          // we are not willing to continue on any other error
-          nsHandleSSLError(mInfoObject, i_node->error);
-          // this error is our stop condition, so let's make sure
-          // this error code will be reported to the external world.
-          mErrorCodeToReport = i_node->error;
-          mInfoObject->SetCanceled(true);
-          return;
-      }
-    }
-  }
-
-  if (!collected_errors)
-  {
-    NS_NOTREACHED("why did NSS call our bad cert handler if all looks good? Let's cancel the connection");
-    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] !collected_errors\n",
-           mFdForLogging, this));
-    return;
-  }
-
-  nsRefPtr<nsSSLStatus> status = mInfoObject->SSLStatus();
-  if (!status) {
-    status = new nsSSLStatus();
-    mInfoObject->SetSSLStatus(status);
-  }
-
-  if (status) {
-    if (!status->mServerCert) {
-      status->mServerCert = mCert;
-    }
-
-    status->mHaveCertErrorBits = true;
-    status->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH;
-    status->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME;
-    status->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
-
-    nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
-      mInfoObject, status, SECFailure);
-  }
-
-  nsDependentCString hostString(mHostname);
-
   PRInt32 port;
   mInfoObject->GetPort(&port);
 
-  nsCString hostWithPortString = hostString;
+  nsCString hostWithPortString;
+  hostWithPortString.AppendASCII(mInfoObject->GetHostName());
   hostWithPortString.AppendLiteral(":");
   hostWithPortString.AppendInt(port);
 
-  NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
-
-  PRUint32 remaining_display_errors = collected_errors;
+  PRUint32 remaining_display_errors = mCollectedErrors;
 
   nsresult nsrv;
 
   // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
   // connections must be dropped when there are any certificate errors
   // (STS Spec section 7.3).
   bool strictTransportSecurityEnabled = false;
   nsCOMPtr<nsIStrictTransportSecurityService> stss
     = do_GetService(NS_STSSERVICE_CONTRACTID, &nsrv);
   if (NS_SUCCEEDED(nsrv)) {
-    nsrv = stss->IsStsHost(mHostname, &strictTransportSecurityEnabled);
+    nsrv = stss->IsStsHost(mInfoObject->GetHostName(),
+                           &strictTransportSecurityEnabled);
   }
-  if (NS_FAILED(nsrv))
-    return; // use default rv and errorCodeToReport
+  if (NS_FAILED(nsrv)) {
+    return new SSLServerCertVerificationResult(*mInfoObject,
+                                               mDefaultErrorCodeToReport);
+  }
 
   if (!strictTransportSecurityEnabled) {
     nsCOMPtr<nsICertOverrideService> overrideService =
       do_GetService(NS_CERTOVERRIDE_CONTRACTID);
     // it is fine to continue without the nsICertOverrideService
 
     PRUint32 overrideBits = 0;
 
     if (overrideService)
     {
       bool haveOverride;
       bool isTemporaryOverride; // we don't care
-
+      nsCString hostString(mInfoObject->GetHostName());
       nsrv = overrideService->HasMatchingOverride(hostString, port,
                                                   mCert,
                                                   &overrideBits,
                                                   &isTemporaryOverride, 
                                                   &haveOverride);
       if (NS_SUCCEEDED(nsrv) && haveOverride) 
       {
-        // remove the errors that are already overriden
+       // remove the errors that are already overriden
         remaining_display_errors -= overrideBits;
       }
     }
 
     if (!remaining_display_errors) {
       // all errors are covered by override rules, so let's accept the cert
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
              ("[%p][%p] All errors covered by override rules\n",
              mFdForLogging, this));
-      mRv = SECSuccess;
-      return;
+      return new SSLServerCertVerificationResult(*mInfoObject, 0);
     }
   } else {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("[%p][%p] Strict-Transport-Security is violated: untrusted "
             "transport layer\n", mFdForLogging, this));
   }
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
          ("[%p][%p] Certificate error was not overridden\n",
          mFdForLogging, this));
 
   // Ok, this is a full stop.
-  // First, deliver the technical details of the broken SSL status,
-  // giving the caller a chance to suppress the error messages.
-
-  bool suppressMessage = false;
+  // First, deliver the technical details of the broken SSL status.
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
   nsCOMPtr<nsIInterfaceRequestor> cb;
   mInfoObject->GetNotificationCallbacks(getter_AddRefs(cb));
   if (cb) {
     nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
     if (bcl) {
       nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(mInfoObject);
-      nsrv = bcl->NotifyCertProblem(csi, status, hostWithPortString, &suppressMessage);
+      bool suppressMessage = false; // obsolete, ignored
+      nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
+                                    hostWithPortString, &suppressMessage);
     }
   }
 
   nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService = 
     do_GetService(NS_RECENTBADCERTS_CONTRACTID);
-
+ 
   if (recentBadCertsService) {
-    recentBadCertsService->AddBadCert(hostWithPortStringUTF16, status);
+    NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
+    recentBadCertsService->AddBadCert(hostWithPortStringUTF16,
+                                      mInfoObject->SSLStatus());
   }
 
   // pick the error code to report by priority
-  mErrorCodeToReport = 0;
-  if (remaining_display_errors & nsICertOverrideService::ERROR_UNTRUSTED)
-    mErrorCodeToReport = errorCodeTrust;
-  else if (remaining_display_errors & nsICertOverrideService::ERROR_MISMATCH)
-    mErrorCodeToReport = errorCodeMismatch;
-  else if (remaining_display_errors & nsICertOverrideService::ERROR_TIME)
-    mErrorCodeToReport = errorCodeExpired;
-
-  if (!suppressMessage && mInfoObject->GetExternalErrorReporting()) {
-    NS_ConvertASCIItoUTF16 hostU(hostString);
-    NS_ConvertASCIItoUTF16 hostWithPortU(hostWithPortString);
-    nsString formattedString;
-    getInvalidCertErrorMessage(remaining_display_errors, mErrorCodeToReport,
-                               errorCodeTrust, errorCodeMismatch,
-                               errorCodeExpired, hostU, hostWithPortU, port,
-                               mCert, formattedString);
-    mInfoObject->SetErrorMessage(formattedString.get());
+  PRErrorCode errorCodeToReport = mErrorCodeTrust    ? mErrorCodeTrust
+                                : mErrorCodeMismatch ? mErrorCodeMismatch
+                                : mErrorCodeExpired  ? mErrorCodeExpired
+                                : mDefaultErrorCodeToReport;
+
+  return new SSLServerCertVerificationResult(*mInfoObject, errorCodeToReport,
+                                             OverridableCertErrorMessage);
+}
+
+NS_IMETHODIMP
+CertErrorRunnable::Run()
+{
+  // This code is confusing: First, Run() is called on the socket transport
+  // thread. Then we re-dispatch it to the main thread synchronously (step 1).
+  // On the main thread, we call CheckCertOverrides (step 2). Then we return
+  // from the main thread and are back on the socket transport thread. There,
+  // we run the result runnable directly (step 3).
+  if (!NS_IsMainThread()) {
+    // We are running on the socket transport thread. We need to re-dispatch
+    // ourselves synchronously to the main thread.
+    DispatchToMainThreadAndWait(); // step 1
+
+    // step 3
+    if (!mResult) {
+      // Either the dispatch failed or CheckCertOverrides wrongly returned null
+      NS_ERROR("Did not create a SSLServerCertVerificationResult");
+      mResult = new SSLServerCertVerificationResult(*mInfoObject,
+                                                    PR_INVALID_STATE_ERROR);
+    }
+    return mResult->Run(); 
+  } else {
+    // block this thread (the socket transport thread) until RunOnTargetThread
+    // is complete.
+    return SyncRunnableBase::Run(); // step 2
   }
-
-  mInfoObject->SetCanceled(true);
 }
 
+void 
+CertErrorRunnable::RunOnTargetThread()
+{
+  // Now we are running on the main thread, blocking the socket tranposrt
+  // thread. This is exactly the state we need to be in to call
+  // CheckCertOverrides; CheckCertOverrides must block events on both of
+  // these threads because it calls nsNSSSocketInfo::GetInterface(),
+  // which calls nsHttpConnection::GetInterface() through
+  // nsNSSSocketInfo::mCallbacks. nsHttpConnection::GetInterface must always
+  // execute on the main thread, with the socket transport thread blocked.
+  mResult = CheckCertOverrides(); 
+}
+ 
 static PRFileDesc*
 nsSSLIOLayerImportFD(PRFileDesc *fd,
                      nsNSSSocketInfo *infoObject,
                      const char *host,
                      bool anonymousLoad)
 {
   nsNSSShutDownPreventionLock locker;
   PRFileDesc* sslSock = SSL_ImportFD(nsnull, fd);
@@ -3612,21 +3815,24 @@ nsSSLIOLayerImportFD(PRFileDesc *fd,
   // Disable this hook if we connect anonymously. See bug 466080.
   if (anonymousLoad) {
       SSL_GetClientAuthDataHook(sslSock, NULL, infoObject);
   } else {
       SSL_GetClientAuthDataHook(sslSock, 
                             (SSLGetClientAuthData)nsNSS_SSLGetClientAuthData,
                             infoObject);
   }
-  SSL_AuthCertificateHook(sslSock, AuthCertificateCallback, 0);
-
-  PRInt32 ret = SSL_SetURL(sslSock, host);
-  if (ret == -1) {
-    NS_ASSERTION(false, "NSS: Error setting server name");
+  if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
+                                            infoObject)) {
+    NS_NOTREACHED("failed to configure AuthCertificateHook");
+    goto loser;
+  }
+
+  if (SECSuccess != SSL_SetURL(sslSock, host)) {
+    NS_NOTREACHED("SSL_SetURL failed");
     goto loser;
   }
   return sslSock;
 loser:
   if (sslSock) {
     PR_Close(sslSock);
   }
   return nsnull;
@@ -3662,20 +3868,16 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, b
     // One advantage of this approach, if a site only supports the older
     // hellos, it is more likely that we will get a reasonable error code
     // on our single retry attempt.
   }
 
   if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
     return NS_ERROR_FAILURE;
   }
-  if (SECSuccess != SSL_BadCertHook(fd, (SSLBadCertHandler) nsNSSBadCertHandler,
-                                    infoObject)) {
-    return NS_ERROR_FAILURE;
-  }
   
   if (nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(nsDependentCString(host))) {
     if (SECSuccess != SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION, false)) {
       return NS_ERROR_FAILURE;
     }
     if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED)) {
       return NS_ERROR_FAILURE;
     }
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -55,81 +55,30 @@
 #include "nsIAssociatedContentSecurity.h"
 #include "nsXPIDLString.h"
 #include "nsNSSShutDown.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsAutoPtr.h"
 #include "nsNSSCertificate.h"
 #include "nsDataHashtable.h"
 
-class nsIChannel;
-class nsSSLThread;
-
-/*
- * This class is used to store SSL socket I/O state information,
- * that is not being executed directly, but defered to 
- * the separate SSL thread.
- */
-class nsSSLSocketThreadData
-{
-public:
-  nsSSLSocketThreadData();
-  ~nsSSLSocketThreadData();
+namespace mozilla {
 
-  bool ensure_buffer_size(PRInt32 amount);
-  
-  enum ssl_state { 
-    ssl_invalid,       // used for initializating, should never occur
-    ssl_idle,          // not in use by SSL thread, no activity pending
-    ssl_pending_write, // waiting for SSL thread to complete writing
-    ssl_pending_read,  // waiting for SSL thread to complete reading
-    ssl_writing_done,  // SSL write completed, results are ready
-    ssl_reading_done   // SSL read completed, results are ready
-  };
-  
-  ssl_state mSSLState;
+class MutexAutoLock;
 
-  // Used to transport I/O error codes between SSL thread
-  // and initial caller thread.
-  PRErrorCode mPRErrorCode;
+namespace psm {
 
-  // A buffer used to transfer I/O data between threads
-  char *mSSLDataBuffer;
-  PRInt32 mSSLDataBufferAllocatedSize;
-
-  // The amount requested to read or write by the caller.
-  PRInt32 mSSLRequestedTransferAmount;
+enum SSLErrorMessageType {
+  OverridableCertErrorMessage  = 1, // for *overridable* certificate errors
+  PlainErrorMessage = 2             // all other errors (or "no error")
+};
 
-  // A pointer into our buffer, to the first byte
-  // that has not yet been delivered to the caller.
-  // Necessary, as the caller of the read function
-  // might request smaller chunks.
-  const char *mSSLRemainingReadResultData;
-  
-  // The caller previously requested to read or write.
-  // As the initial request to read or write is defered,
-  // the caller might (in theory) request smaller chunks
-  // in subsequent calls.
-  // This variable stores the amount of bytes successfully
-  // transfered, that have not yet been reported to the caller.
-  PRInt32 mSSLResultRemainingBytes;
+} // namespace psm
 
-  // When defering SSL read/write activity to another thread,
-  // we switch the SSL level file descriptor of the original
-  // layered file descriptor to a pollable event,
-  // so we can wake up the original caller of the I/O function
-  // as soon as data is ready.
-  // This variable is used to save the SSL level file descriptor,
-  // to allow us to restore the original file descriptor layering.
-  PRFileDesc *mReplacedSSLFileDesc;
-
-  bool mOneBytePendingFromEarlierWrite;
-  unsigned char mThePendingByte;
-  PRInt32 mOriginalRequestedTransferAmount;
-};
+} // namespace mozilla
 
 class nsNSSSocketInfo : public nsITransportSecurityInfo,
                         public nsISSLSocketControl,
                         public nsIInterfaceRequestor,
                         public nsISSLStatusProvider,
                         public nsIAssociatedContentSecurity,
                         public nsISerializable,
                         public nsIClassInfo,
@@ -148,102 +97,137 @@ public:
   NS_DECL_NSISSLSTATUSPROVIDER
   NS_DECL_NSIASSOCIATEDCONTENTSECURITY
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
   NS_DECL_NSICLIENTAUTHUSERDECISION
 
   nsresult SetSecurityState(PRUint32 aState);
   nsresult SetShortSecurityDescription(const PRUnichar *aText);
-  void SetErrorMessage(const PRUnichar *aText);
 
   nsresult SetForSTARTTLS(bool aForSTARTTLS);
   nsresult GetForSTARTTLS(bool *aForSTARTTLS);
 
   nsresult GetFileDescPtr(PRFileDesc** aFilePtr);
   nsresult SetFileDescPtr(PRFileDesc* aFilePtr);
 
   nsresult GetHandshakePending(bool *aHandshakePending);
   nsresult SetHandshakePending(bool aHandshakePending);
 
+  const char * GetHostName() const {
+    return mHostName.get();
+  }
   nsresult GetHostName(char **aHostName);
   nsresult SetHostName(const char *aHostName);
 
   nsresult GetPort(PRInt32 *aPort);
   nsresult SetPort(PRInt32 aPort);
 
   void GetPreviousCert(nsIX509Cert** _result);
 
-  void SetCanceled(bool aCanceled);
-  bool GetCanceled();
+  PRErrorCode GetErrorCode() const;
+  void SetCanceled(PRErrorCode errorCode,
+                   ::mozilla::psm::SSLErrorMessageType errorMessageType);
   
   void SetHasCleartextPhase(bool aHasCleartextPhase);
   bool GetHasCleartextPhase();
   
   void SetHandshakeInProgress(bool aIsIn);
   bool GetHandshakeInProgress() { return mHandshakeInProgress; }
   bool HandshakeTimeout();
 
   void SetAllowTLSIntoleranceTimeout(bool aAllow);
 
-  bool GetExternalErrorReporting();
-
   nsresult RememberCAChain(CERTCertList *aCertList);
 
   /* Set SSL Status values */
   nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
   nsSSLStatus* SSLStatus() { return mSSLStatus; }
-  
-  PRStatus CloseSocketAndDestroy();
+  void SetStatusErrorBits(nsIX509Cert & cert, PRUint32 collected_errors);
+
+  PRStatus CloseSocketAndDestroy(
+                const nsNSSShutDownPreventionLock & proofOfLock);
   
   bool IsCertIssuerBlacklisted() const {
     return mIsCertIssuerBlacklisted;
   }
   void SetCertIssuerBlacklisted() {
     mIsCertIssuerBlacklisted = true;
   }
+
+  void SetNegotiatedNPN(const char *value, PRUint32 length);
+  void SetHandshakeCompleted() { mHandshakeCompleted = true; }
+
+  bool GetJoined() { return mJoined; }
+  void SetSentClientCert() { mSentClientCert = true; }
+  
+  // XXX: These are only used on for diagnostic purposes
+  enum CertVerificationState {
+    before_cert_verification,
+    waiting_for_cert_verification,
+    after_cert_verification
+  };
+  void SetCertVerificationWaiting();
+  // Use errorCode == 0 to indicate success; in that case, errorMessageType is
+  // ignored.
+  void SetCertVerificationResult(PRErrorCode errorCode,
+              ::mozilla::psm::SSLErrorMessageType errorMessageType);
+  
+  // for logging only
+  PRBool IsWaitingForCertVerification() const
+  {
+    return mCertVerificationState == waiting_for_cert_verification;
+  }
+  
+
 protected:
+  mutable ::mozilla::Mutex mMutex;
+
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   PRFileDesc* mFd;
-  enum { 
-    blocking_state_unknown, is_nonblocking_socket, is_blocking_socket 
-  } mBlockingState;
+  CertVerificationState mCertVerificationState;
   PRUint32 mSecurityState;
   PRInt32 mSubRequestsHighSecurity;
   PRInt32 mSubRequestsLowSecurity;
   PRInt32 mSubRequestsBrokenSecurity;
   PRInt32 mSubRequestsNoSecurity;
   nsString mShortDesc;
-  nsString mErrorMessage;
+
+  PRErrorCode mErrorCode;
+  ::mozilla::psm::SSLErrorMessageType mErrorMessageType;
+  nsString mErrorMessageCached;
+  nsresult formatErrorMessage(::mozilla::MutexAutoLock const & proofOfLock);
+
   bool mDocShellDependentStuffKnown;
   bool mExternalErrorReporting; // DocShellDependent
   bool mForSTARTTLS;
   bool mHandshakePending;
-  bool mCanceled;
   bool mHasCleartextPhase;
   bool mHandshakeInProgress;
   bool mAllowTLSIntoleranceTimeout;
   bool mRememberClientAuthCertificate;
   PRIntervalTime mHandshakeStartTime;
   PRInt32 mPort;
   nsXPIDLCString mHostName;
   PRErrorCode mIsCertIssuerBlacklisted;
 
   /* SSL Status */
   nsRefPtr<nsSSLStatus> mSSLStatus;
 
   nsresult ActivateSSL();
 
-  nsSSLSocketThreadData *mThreadData;
+  nsCString mNegotiatedNPN;
+  bool      mNPNCompleted;
+  bool      mHandshakeCompleted;
+  bool      mJoined;
+  bool      mSentClientCert;
 
 private:
   virtual void virtualDestroyNSSReference();
   void destructorSafeDestroyNSSReference();
-
-friend class nsSSLThread;
 };
 
 class nsCStringHashSet;
 
 class nsSSLStatus;
 class nsNSSSocketInfo;
 
 class nsPSMRememberCertErrorsTable
@@ -298,21 +282,16 @@ public:
   static void rememberTolerantSite(PRFileDesc* ssl_layer_fd, nsNSSSocketInfo *socketInfo);
 
   static void addIntolerantSite(const nsCString &str);
   static void removeIntolerantSite(const nsCString &str);
   static bool isKnownAsIntolerantSite(const nsCString &str);
 
   static void setRenegoUnrestrictedSites(const nsCString &str);
   static bool isRenegoUnrestrictedSite(const nsCString &str);
-
-  static PRFileDesc *mSharedPollableEvent;
-  static nsNSSSocketInfo *mSocketOwningPollableEvent;
-  
-  static bool mPollableEventCurrentlySet;
 };
 
 nsresult nsSSLIOLayerNewSocket(PRInt32 family,
                                const char *host,
                                PRInt32 port,
                                const char *proxyHost,
                                PRInt32 proxyPort,
                                PRFileDesc **fd,
--- a/security/manager/ssl/src/nsSSLStatus.h
+++ b/security/manager/ssl/src/nsSSLStatus.h
@@ -69,16 +69,19 @@ public:
   PRUint32 mSecretKeyLength;
   nsXPIDLCString mCipherName;
 
   bool mIsDomainMismatch;
   bool mIsNotValidAtThisTime;
   bool mIsUntrusted;
 
   bool mHaveKeyLengthAndCipher;
+
+  /* mHaveCertErrrorBits is relied on to determine whether or not a SPDY
+     connection is eligible for joining in nsNSSSocketInfo::JoinConnection() */
   bool mHaveCertErrorBits;
 };
 
 // 2c3837af-8b85-4a68-b0d8-0aed88985b32
 #define NS_SSLSTATUS_CID \
 { 0x2c3837af, 0x8b85, 0x4a68, \
   { 0xb0, 0xd8, 0x0a, 0xed, 0x88, 0x98, 0x5b, 0x32 } }
 
deleted file mode 100644
--- a/security/manager/ssl/src/nsSSLThread.cpp
+++ /dev/null
@@ -1,1150 +0,0 @@
-/* ***** 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
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Kai Engert <kengert@redhat.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 "nsThreadUtils.h"
-#include "nsSSLThread.h"
-#include "nsNSSIOLayer.h"
-
-#include "ssl.h"
-
-using namespace mozilla;
-
-#ifdef PR_LOGGING
-extern PRLogModuleInfo* gPIPNSSLog;
-#endif
-
-nsSSLThread::nsSSLThread()
-: mBusySocket(nsnull),
-  mSocketScheduledToBeDestroyed(nsnull)
-{
-  NS_ASSERTION(!ssl_thread_singleton, "nsSSLThread is a singleton, caller attempts to create another instance!");
-  
-  ssl_thread_singleton = this;
-}
-
-nsSSLThread::~nsSSLThread()
-{
-  ssl_thread_singleton = nsnull;
-}
-
-PRFileDesc *nsSSLThread::getRealSSLFD(nsNSSSocketInfo *si)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-    return nsnull;
-
-  MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-  if (si->mThreadData->mReplacedSSLFileDesc)
-  {
-    return si->mThreadData->mReplacedSSLFileDesc;
-  }
-  else
-  {
-    return si->mFd->lower;
-  }
-}
-
-PRStatus nsSSLThread::requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getsockname(fd, addr);
-}
-
-PRStatus nsSSLThread::requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getpeername(fd, addr);
-}
-
-PRStatus nsSSLThread::requestGetsocketoption(nsNSSSocketInfo *si, 
-                                             PRSocketOptionData *data)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->getsocketoption(fd, data);
-}
-
-PRStatus nsSSLThread::requestSetsocketoption(nsNSSSocketInfo *si, 
-                                             const PRSocketOptionData *data)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->setsocketoption(fd, data);
-}
-
-PRStatus nsSSLThread::requestConnectcontinue(nsNSSSocketInfo *si, 
-                                             PRInt16 out_flags)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return PR_FAILURE;
-
-  return fd->methods->connectcontinue(fd, out_flags);
-}
-
-PRInt32 nsSSLThread::requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount,
-                                        PRIntn flags, PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
-    return -1;
-  }
-
-  // Socket is unusable - set error and return -1. See bug #480619.
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown())
-  {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  PRFileDesc *realSSLFD;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (si == ssl_thread_singleton->mBusySocket)
-    {
-      PORT_SetError(PR_WOULD_BLOCK_ERROR);
-      return -1;
-    }
-
-    switch (si->mThreadData->mSSLState)
-    {
-      case nsSSLSocketThreadData::ssl_idle:
-        break;
-    
-      case nsSSLSocketThreadData::ssl_reading_done:
-        {
-          // we have data available that we can return
-
-          // if there was a failure, just return the failure,
-          // but do not yet clear our state, that should happen
-          // in the call to "read".
-
-          if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-            if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-              PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            }
-
-            return si->mThreadData->mSSLResultRemainingBytes;
-          }
-
-          PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-          memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
-
-          return return_amount;
-        }
-
-      case nsSSLSocketThreadData::ssl_writing_done:
-      case nsSSLSocketThreadData::ssl_pending_write:
-      case nsSSLSocketThreadData::ssl_pending_read:
-
-      // for safety reasons, also return would_block on any other state,
-      // although this switch statement should be complete and list
-      // the appropriate behaviour for each state.
-      default:
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-    }
-
-    if (si->mThreadData->mReplacedSSLFileDesc)
-    {
-      realSSLFD = si->mThreadData->mReplacedSSLFileDesc;
-    }
-    else
-    {
-      realSSLFD = si->mFd->lower;
-    }
-  }
-
-  return realSSLFD->methods->recv(realSSLFD, buf, amount, flags, timeout);
-}
-
-nsresult nsSSLThread::requestActivateSSL(nsNSSSocketInfo *si)
-{
-  PRFileDesc *fd = getRealSSLFD(si);
-  if (!fd)
-    return NS_ERROR_FAILURE;
-
-  if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, true))
-    return NS_ERROR_FAILURE;
-
-  if (SECSuccess != SSL_ResetHandshake(fd, false))
-    return NS_ERROR_FAILURE;
-
-  return NS_OK;
-}
-
-PRInt16 nsSSLThread::requestPoll(nsNSSSocketInfo *si, PRInt16 in_flags, PRInt16 *out_flags)
-{
-  if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle)
-    return 0;
-
-  *out_flags = 0;
-
-  // Socket is unusable - set EXCEPT-flag and return. See bug #480619.
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown())
-  {
-    *out_flags |= PR_POLL_EXCEPT;
-    return in_flags;
-  }
-
-  bool want_sleep_and_wakeup_on_any_socket_activity = false;
-  bool handshake_timeout = false;
-  
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->mBusySocket)
-    {
-      // If there is currently any socket busy on the SSL thread,
-      // use our own poll method implementation.
-      
-      switch (si->mThreadData->mSSLState)
-      {
-        case nsSSLSocketThreadData::ssl_writing_done:
-        {
-          if (in_flags & PR_POLL_WRITE)
-          {
-            *out_flags |= PR_POLL_WRITE;
-          }
-
-          return in_flags;
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_reading_done:
-        {
-          if (in_flags & PR_POLL_READ)
-          {
-            *out_flags |= PR_POLL_READ;
-          }
-
-          return in_flags;
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_pending_write:
-        case nsSSLSocketThreadData::ssl_pending_read:
-        {
-          if (si == ssl_thread_singleton->mBusySocket)
-          {
-            if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-            {
-              // The lower layer of the socket is currently the pollable event,
-              // which signals the readable state.
-              
-              return PR_POLL_READ;
-            }
-            else
-            {
-              // Unfortunately we do not have a pollable event
-              // that we could use to wake up the caller, as soon
-              // as the previously requested I/O operation has completed.
-              // Therefore we must use a kind of busy wait,
-              // we want the caller to check again, whenever any
-              // activity is detected on the associated socket.
-              // Unfortunately this could mean, the caller will detect
-              // activity very often, until we are finally done with
-              // the previously requested action and are able to
-              // return the buffered result.
-              // As our real I/O activity is happening on the other thread
-              // let's sleep some cycles, in order to not waste all CPU
-              // resources.
-              // But let's make sure we do not hold our shared mutex
-              // while waiting, so let's leave this block first.
-
-              want_sleep_and_wakeup_on_any_socket_activity = true;
-              break;
-            }
-          }
-          else
-          {
-            // We should never get here, well, at least not with the current
-            // implementation of SSL thread, where we have one worker only.
-            // While another socket is busy, this socket "si" 
-            // can not be marked with pending I/O at the same time.
-            
-            NS_NOTREACHED("Socket not busy on SSL thread marked as pending");
-            return 0;
-          }
-        }
-        break;
-        
-        case nsSSLSocketThreadData::ssl_idle:
-        {
-          if (si->mThreadData->mOneBytePendingFromEarlierWrite)
-          {
-            if (in_flags & PR_POLL_WRITE)
-            {
-              // In this scenario we always want the caller to immediately
-              // try a write again, because it might not wake up otherwise.
-              *out_flags |= PR_POLL_WRITE;
-              return in_flags;
-            }
-          }
-
-          handshake_timeout = si->HandshakeTimeout();
-
-          if (si != ssl_thread_singleton->mBusySocket)
-          {
-            // Some other socket is currently busy on the SSL thread.
-            // It is possible that busy socket is currently blocked (e.g. by UI).
-            // Therefore we should not report "si" as being readable/writeable,
-            // regardless whether it is.
-            // (Because if we did report readable/writeable to the caller,
-            // the caller would repeatedly request us to do I/O, 
-            // although our read/write function would not be able to fulfil
-            // the request, because our single worker is blocked).
-            // To avoid the unnecessary busy loop in that scenario, 
-            // for socket "si" we report "not ready" to the caller.
-            // We do this by faking our caller did not ask for neither
-            // readable nor writeable when querying the lower layer.
-            // (this will leave querying for exceptions enabled)
-            
-            in_flags &= ~(PR_POLL_READ | PR_POLL_WRITE);
-          }
-        }
-        break;
-        
-        default:
-          break;
-      }
-    }
-    else
-    {
-      handshake_timeout = si->HandshakeTimeout();
-    }
-
-    if (handshake_timeout)
-    {
-      NS_ASSERTION(in_flags & PR_POLL_EXCEPT, "nsSSLThread::requestPoll handshake timeout, but caller did not poll for EXCEPT");
-
-      *out_flags |= PR_POLL_EXCEPT;
-      return in_flags;
-    }
-  }
-
-  if (want_sleep_and_wakeup_on_any_socket_activity)
-  {
-    // This is where we wait for any socket activity,
-    // because we do not have a pollable event.
-    // XXX Will this really cause us to wake up
-    //     whatever happens?
-
-    PR_Sleep( PR_MillisecondsToInterval(1) );
-    return PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT;
-  }
-
-  return si->mFd->lower->methods->poll(si->mFd->lower, in_flags, out_flags);
-}
-
-PRStatus nsSSLThread::requestClose(nsNSSSocketInfo *si)
-{
-  if (!ssl_thread_singleton || !si)
-    return PR_FAILURE;
-
-  bool close_later = false;
-  nsCOMPtr<nsIRequest> requestToCancel;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->mBusySocket == si) {
-    
-      // That's tricky, SSL thread is currently busy with this socket,
-      // and might even be blocked on it (UI or OCSP).
-      // We should not close the socket directly, but rather
-      // schedule closing it, at the time the SSL thread is done.
-      // If there is indeed a depending OCSP request pending,
-      // we should cancel it now.
-      
-      if (ssl_thread_singleton->mPendingHTTPRequest)
-      {
-        requestToCancel.swap(ssl_thread_singleton->mPendingHTTPRequest);
-      }
-      
-      close_later = true;
-      ssl_thread_singleton->mSocketScheduledToBeDestroyed = si;
-
-      ssl_thread_singleton->mCond.NotifyAll();
-    }
-  }
-
-  if (requestToCancel)
-  {
-    if (NS_IsMainThread())
-    {
-      requestToCancel->Cancel(NS_ERROR_ABORT);
-    }
-    else
-    {
-      NS_WARNING("Attempt to close SSL socket from a thread that is not the main thread. Can not cancel pending HTTP request from NSS");
-    }
-  
-    requestToCancel = nsnull;
-  }
-  
-  if (!close_later)
-  {
-    return si->CloseSocketAndDestroy();
-  }
-  
-  return PR_SUCCESS;
-}
-
-void nsSSLThread::restoreOriginalSocket_locked(nsNSSSocketInfo *si)
-{
-  if (si->mThreadData->mReplacedSSLFileDesc)
-  {
-    if (nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-    {
-      nsSSLIOLayerHelpers::mPollableEventCurrentlySet = false;
-      if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-      {
-        PR_WaitForPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-      }
-    }
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      // need to restore
-      si->mFd->lower = si->mThreadData->mReplacedSSLFileDesc;
-      si->mThreadData->mReplacedSSLFileDesc = nsnull;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull;
-  }
-}
-
-PRStatus nsSSLThread::getRealFDIfBlockingSocket_locked(nsNSSSocketInfo *si, 
-                                                       PRFileDesc *&out_fd)
-{
-  out_fd = nsnull;
-
-  PRFileDesc *realFD = 
-    (si->mThreadData->mReplacedSSLFileDesc) ?
-      si->mThreadData->mReplacedSSLFileDesc : si->mFd->lower;
-
-  if (si->mBlockingState == nsNSSSocketInfo::blocking_state_unknown)
-  {
-    PRSocketOptionData sod;
-    sod.option = PR_SockOpt_Nonblocking;
-    if (PR_GetSocketOption(realFD, &sod) == PR_FAILURE)
-      return PR_FAILURE;
-
-    si->mBlockingState = sod.value.non_blocking ?
-      nsNSSSocketInfo::is_nonblocking_socket : nsNSSSocketInfo::is_blocking_socket;
-  }
-
-  if (si->mBlockingState == nsNSSSocketInfo::is_blocking_socket)
-  {
-    out_fd = realFD;
-  }
-
-  return PR_SUCCESS;
-}
-
-PRInt32 nsSSLThread::requestRead(nsNSSSocketInfo *si, void *buf, PRInt32 amount, 
-                                 PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return -1;
-  }
-
-  bool this_socket_is_busy = false;
-  bool some_other_socket_is_busy = false;
-  nsSSLSocketThreadData::ssl_state my_ssl_state = nsSSLSocketThreadData::ssl_invalid;
-  PRFileDesc *blockingFD = nsnull;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (ssl_thread_singleton->exitRequested(threadLock)) {
-      PR_SetError(PR_UNKNOWN_ERROR, 0);
-      return -1;
-    }
-
-    if (getRealFDIfBlockingSocket_locked(si, blockingFD) == PR_FAILURE) {
-      return -1;
-    }
-
-    if (!blockingFD)
-    {
-      my_ssl_state = si->mThreadData->mSSLState;
-  
-      if (ssl_thread_singleton->mBusySocket == si)
-      {
-        this_socket_is_busy = true;
-  
-        if (my_ssl_state == nsSSLSocketThreadData::ssl_reading_done)
-        {
-          // we will now care for the data that's ready,
-          // the socket is no longer busy on the ssl thread
-          
-          restoreOriginalSocket_locked(si);
-  
-          ssl_thread_singleton->mBusySocket = nsnull;
-          
-          // We'll handle the results further down,
-          // while not holding the lock.
-        }
-      }
-      else if (ssl_thread_singleton->mBusySocket)
-      {
-        some_other_socket_is_busy = true;
-      }
-  
-      if (!this_socket_is_busy && si->HandshakeTimeout())
-      {
-        restoreOriginalSocket_locked(si);
-        PR_SetError(PR_CONNECT_RESET_ERROR, 0);
-        checkHandshake(-1, true, si->mFd->lower, si);
-        return -1;
-      }
-    }
-    // leave this mutex protected scope before the blockingFD handling
-  }
-
-  if (blockingFD)
-  {
-    // this is an exception, we do not use our SSL thread at all,
-    // just pass the call through to libssl.
-    return blockingFD->methods->recv(blockingFD, buf, amount, 0, timeout);
-  }
-
-  switch (my_ssl_state)
-  {
-    case nsSSLSocketThreadData::ssl_idle:
-      {
-        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
-        
-        if (some_other_socket_is_busy)
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-        
-        // ssl thread is not busy, we'll continue below
-      }
-      break;
-
-    case nsSSLSocketThreadData::ssl_reading_done:
-      // there has been a previous request to read, that is now done!
-      {
-        // failure ?
-        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-            PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            si->mThreadData->mPRErrorCode = PR_SUCCESS;
-          }
-
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          return si->mThreadData->mSSLResultRemainingBytes;
-        }
-
-        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-        memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount);
-
-        si->mThreadData->mSSLResultRemainingBytes -= return_amount;
-
-        if (!si->mThreadData->mSSLResultRemainingBytes) {
-          si->mThreadData->mSSLRemainingReadResultData = nsnull;
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-        }
-        else {
-          si->mThreadData->mSSLRemainingReadResultData += return_amount;
-        }
-
-        return return_amount;
-      }
-      // we never arrive here, see return statement above
-      break;
-
-
-    // We should not see the following events here,
-    // because we have not yet signaled Necko that we are 
-    // readable/writable again, so if we end up here,
-    // it means that Necko decided to try read/write again,
-    // for whatever reason. No problem, just return would_block,
-    case nsSSLSocketThreadData::ssl_pending_write:
-    case nsSSLSocketThreadData::ssl_pending_read:
-
-    // We should not see this state here, because Necko has previously
-    // requested us to write, Necko is not yet aware that it's done,
-    // (although it meanwhile is), but Necko now tries to read?
-    // If that ever happens, it's confusing, but not a problem,
-    // just let Necko know we can not do that now and return would_block.
-    case nsSSLSocketThreadData::ssl_writing_done:
-
-    // for safety reasons, also return would_block on any other state,
-    // although this switch statement should be complete and list
-    // the appropriate behaviour for each state.
-    default:
-      {
-        PORT_SetError(PR_WOULD_BLOCK_ERROR);
-        return -1;
-      }
-      // we never arrive here, see return statement above
-      break;
-  }
-
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  if (si->GetCanceled()) {
-    return PR_FAILURE;
-  }
-
-  // si is idle and good, and no other socket is currently busy,
-  // so it's fine to continue with the request.
-
-  if (!si->mThreadData->ensure_buffer_size(amount))
-  {
-    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
-    return -1;
-  }
-  
-  si->mThreadData->mSSLRequestedTransferAmount = amount;
-  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_read;
-
-  // Remember we are operating on a layered file descriptor, that consists of
-  // a PSM code layer (nsNSSIOLayer), a NSS code layer (SSL protocol logic), 
-  // and the raw socket at the bottommost layer.
-  //
-  // We don't want to call the SSL layer read/write directly on this thread, 
-  // because it might block, should a callback to UI (for user confirmation)
-  // or Necko (for retrieving OCSP verification data) be necessary.
-  // As Necko is single threaded, it is currently waiting for this 
-  // function to return, and a callback into Necko from NSS couldn't succeed.
-  // 
-  // Therefore we must defer the request to read/write to a separate SSL thread.
-  // We will return WOULD_BLOCK to Necko, and will return the real results
-  // once the I/O operation on the SSL thread is ready.
-  //
-  // The tricky part is to wake up Necko, as soon as the I/O operation 
-  // on the SSL thread is done.
-  //
-  // In order to achieve that, we manipulate the layering of the file 
-  // descriptor. Usually the PSM layer points to the SSL layer as its lower 
-  // layer. We change that to a pollable event file descriptor.
-  //
-  // Once we return from this original read/write function, Necko will 
-  // poll/select on the file descriptor. As result data is not yet ready, we will 
-  // instruct Necko to select on the bottommost file descriptor 
-  // (by using appropriate flags in PSM's layer implementation of the 
-  // poll method), which is the pollable event.
-  //
-  // Once the SSL thread is done with the call to the SSL layer, it will 
-  // "set" the pollable event, causing Necko to wake up on the file descriptor 
-  // and call read/write again. Now that the file descriptor is in the done state, 
-  // we'll arrive in this read/write function again. We'll detect the socket is 
-  // in the done state, and restore the original SSL level file descriptor.
-  // Finally, we return the data obtained on the SSL thread back to our caller.
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
-                   "oops, some other socket still owns our shared pollable event");
-  
-      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
-  
-      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
-      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
-    ssl_thread_singleton->mBusySocket = si;
-
-    // notify the thread
-    ssl_thread_singleton->mCond.NotifyAll();
-  }
-
-  PORT_SetError(PR_WOULD_BLOCK_ERROR);
-  return -1;
-}
-
-PRInt32 nsSSLThread::requestWrite(nsNSSSocketInfo *si, const void *buf, PRInt32 amount,
-                                  PRIntervalTime timeout)
-{
-  if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle)
-  {
-    PR_SetError(PR_UNKNOWN_ERROR, 0);
-    return -1;
-  }
-
-  bool this_socket_is_busy = false;
-  bool some_other_socket_is_busy = false;
-  nsSSLSocketThreadData::ssl_state my_ssl_state = nsSSLSocketThreadData::ssl_invalid;
-  PRFileDesc *blockingFD = nsnull;
-  
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-    
-    if (ssl_thread_singleton->exitRequested(threadLock)) {
-      PR_SetError(PR_UNKNOWN_ERROR, 0);
-      return -1;
-    }
-
-    if (getRealFDIfBlockingSocket_locked(si, blockingFD) == PR_FAILURE) {
-      return -1;
-    }
-
-    if (!blockingFD)
-    {
-      my_ssl_state = si->mThreadData->mSSLState;
-  
-      if (ssl_thread_singleton->mBusySocket == si)
-      {
-        this_socket_is_busy = true;
-        
-        if (my_ssl_state == nsSSLSocketThreadData::ssl_writing_done)
-        {
-          // we will now care for the data that's ready,
-          // the socket is no longer busy on the ssl thread
-          
-          restoreOriginalSocket_locked(si);
-  
-          ssl_thread_singleton->mBusySocket = nsnull;
-          
-          // We'll handle the results further down,
-          // while not holding the lock.
-        }
-      }
-      else if (ssl_thread_singleton->mBusySocket)
-      {
-        some_other_socket_is_busy = true;
-      }
-  
-      if (!this_socket_is_busy && si->HandshakeTimeout())
-      {
-        restoreOriginalSocket_locked(si);
-        PR_SetError(PR_CONNECT_RESET_ERROR, 0);
-        checkHandshake(-1, false, si->mFd->lower, si);
-        return -1;
-      }
-    }
-    // leave this mutex protected scope before the blockingFD handling
-  }
-
-  if (blockingFD)
-  {
-    // this is an exception, we do not use our SSL thread at all,
-    // just pass the call through to libssl.
-    return blockingFD->methods->send(blockingFD, buf, amount, 0, timeout);
-  }
-
-  switch (my_ssl_state)
-  {
-    case nsSSLSocketThreadData::ssl_idle:
-      {
-        NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency");
-        
-        if (some_other_socket_is_busy)
-        {
-          PORT_SetError(PR_WOULD_BLOCK_ERROR);
-          return -1;
-        }
-        
-        // ssl thread is not busy, we'll continue below
-      }
-      break;
-
-    case nsSSLSocketThreadData::ssl_writing_done:
-      // there has been a previous request to write, that is now done!
-      {
-        // failure ?
-        if (si->mThreadData->mSSLResultRemainingBytes < 0) {
-          if (si->mThreadData->mPRErrorCode != PR_SUCCESS) {
-            PR_SetError(si->mThreadData->mPRErrorCode, 0);
-            si->mThreadData->mPRErrorCode = PR_SUCCESS;
-          }
-
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          return si->mThreadData->mSSLResultRemainingBytes;
-        }
-
-        nsSSLIOLayerHelpers::rememberTolerantSite(si->mFd, si);
-
-        PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes);
-
-        si->mThreadData->mSSLResultRemainingBytes -= return_amount;
-
-        if (!si->mThreadData->mSSLResultRemainingBytes) {
-          si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-        }
-
-        return return_amount;
-      }
-      break;
-      
-    // We should not see the following events here,
-    // because we have not yet signaled Necko that we are 
-    // readable/writable again, so if we end up here,
-    // it means that Necko decided to try read/write again,
-    // for whatever reason. No problem, just return would_block,
-    case nsSSLSocketThreadData::ssl_pending_write:
-    case nsSSLSocketThreadData::ssl_pending_read:
-
-    // We should not see this state here, because Necko has previously
-    // requested us to read, Necko is not yet aware that it's done,
-    // (although it meanwhile is), but Necko now tries to write?
-    // If that ever happens, it's confusing, but not a problem,
-    // just let Necko know we can not do that now and return would_block.
-    case nsSSLSocketThreadData::ssl_reading_done:
-
-    // for safety reasons, also return would_block on any other state,
-    // although this switch statement should be complete and list
-    // the appropriate behaviour for each state.
-    default:
-      {
-        PORT_SetError(PR_WOULD_BLOCK_ERROR);
-        return -1;
-      }
-      // we never arrive here, see return statement above
-      break;
-  }
-
-  if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) {
-    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
-    return -1;
-  }
-
-  if (si->GetCanceled()) {
-    return PR_FAILURE;
-  }
-
-  // si is idle and good, and no other socket is currently busy,
-  // so it's fine to continue with the request.
-
-  // However, use special handling for the 
-  //   mOneBytePendingFromEarlierWrite
-  // scenario, where we will not change any of our buffers at this point, 
-  // as we are waiting for completion of the earlier write.
-
-  if (!si->mThreadData->mOneBytePendingFromEarlierWrite)
-  {
-    if (!si->mThreadData->ensure_buffer_size(amount))
-    {
-      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
-      return -1;
-    }
-  
-    memcpy(si->mThreadData->mSSLDataBuffer, buf, amount);
-    si->mThreadData->mSSLRequestedTransferAmount = amount;
-  }
-
-  si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_write;
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-
-    if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, 
-                   "oops, some other socket still owns our shared pollable event");
-  
-      NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops");
-  
-      si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower;
-      si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent;
-    }
-
-    nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si;
-    ssl_thread_singleton->mBusySocket = si;
-
-    ssl_thread_singleton->mCond.NotifyAll();
-  }
-
-  PORT_SetError(PR_WOULD_BLOCK_ERROR);
-  return -1;
-}
-
-void nsSSLThread::Run(void)
-{
-  // Helper variable, we don't want to call destroy 
-  // while holding the mutex.
-  nsNSSSocketInfo *socketToDestroy = nsnull;
-
-  while (true)
-  {
-    if (socketToDestroy)
-    {
-      socketToDestroy->CloseSocketAndDestroy();
-      socketToDestroy = nsnull;
-    }
-
-    // remember whether we'll write or read
-    nsSSLSocketThreadData::ssl_state busy_socket_ssl_state;
-  
-    {
-      // In this scope we need mutex protection,
-      // as we find out what needs to be done.
-      
-      MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-      
-      if (mSocketScheduledToBeDestroyed)
-      {
-        if (mBusySocket == mSocketScheduledToBeDestroyed)
-        {
-          // That's rare, but it happens.
-          // We have received a request to close the socket,
-          // although I/O results have not yet been consumed.
-
-          restoreOriginalSocket_locked(mBusySocket);
-
-          mBusySocket->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle;
-          mBusySocket = nsnull;
-        }
-      
-        socketToDestroy = mSocketScheduledToBeDestroyed;
-        mSocketScheduledToBeDestroyed = nsnull;
-        continue; // go back and finally destroy it, before doing anything else
-      }
-
-      if (exitRequested(threadLock))
-        break;
-
-      bool pending_work = false;
-
-      do
-      {
-        if (mBusySocket
-            &&
-              (mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_read
-              ||
-              mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_write))
-        {
-          pending_work = true;
-        }
-
-        if (!pending_work)
-        {
-          // no work to do ? let's wait a moment
-
-          mCond.Wait();
-        }
-        
-      } while (!pending_work && !exitRequested(threadLock) &&
-               !mSocketScheduledToBeDestroyed);
-      
-      if (mSocketScheduledToBeDestroyed)
-        continue;
-      
-      if (exitRequested(threadLock))
-        break;
-      
-      if (!pending_work)
-        continue;
-      
-      busy_socket_ssl_state = mBusySocket->mThreadData->mSSLState;
-    }
-
-    {
-      // In this scope we need to make sure NSS does not go away
-      // while we are busy.
-      nsNSSShutDownPreventionLock locker;
-
-      // Reference for shorter code and to avoid multiple dereferencing.
-      nsSSLSocketThreadData &bstd = *mBusySocket->mThreadData;
-
-      PRFileDesc *realFileDesc = bstd.mReplacedSSLFileDesc;
-      if (!realFileDesc)
-      {
-        realFileDesc = mBusySocket->mFd->lower;
-      }
-
-      if (nsSSLSocketThreadData::ssl_pending_write == busy_socket_ssl_state)
-      {
-        PRInt32 bytesWritten = 0;
-
-        if (bstd.mOneBytePendingFromEarlierWrite)
-        {
-          // Let's try to flush the final pending byte (that libSSL might already have 
-          // processed). Let's be correct and send the final byte from our buffer.
-          bytesWritten = realFileDesc->methods
-            ->write(realFileDesc, &bstd.mThePendingByte, 1);
-  
-#ifdef DEBUG_SSL_VERBOSE
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)realFileDesc, bytesWritten));
-#endif
-          
-          bytesWritten = checkHandshake(bytesWritten, false, realFileDesc, mBusySocket);
-          if (bytesWritten < 0) {
-            // give the error back to caller
-            bstd.mPRErrorCode = PR_GetError();
-          }
-          else if (bytesWritten == 1) {
-            // Cool, all flushed now. We can exit the one-byte-pending mode, 
-            // and report the full amount back to the caller. 
-            bytesWritten = bstd.mOriginalRequestedTransferAmount;
-            bstd.mOriginalRequestedTransferAmount = 0;
-            bstd.mOneBytePendingFromEarlierWrite = false;
-          }
-        }
-        else
-        {
-          // standard code, try to write the buffer we've been given just now 
-          bytesWritten = realFileDesc->methods
-            ->write(realFileDesc, 
-                    bstd.mSSLDataBuffer, 
-                    bstd.mSSLRequestedTransferAmount);
-  
-#ifdef DEBUG_SSL_VERBOSE
-          PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes (out of %d)\n", 
-              (void*)realFileDesc, bytesWritten, bstd.mSSLRequestedTransferAmount));
-#endif
-          
-          bytesWritten = checkHandshake(bytesWritten, false, realFileDesc, mBusySocket);
-          if (bytesWritten < 0) {
-            // give the error back to caller
-            bstd.mPRErrorCode = PR_GetError();
-          }
-          else if (bstd.mSSLRequestedTransferAmount > 1 && 
-                   bytesWritten == (bstd.mSSLRequestedTransferAmount - 1)) {
-            // libSSL signaled us a short write.
-            // While libSSL accepted all data, not all bytes were flushed to the OS socket.
-            bstd.mThePendingByte = *(bstd.mSSLDataBuffer + (bstd.mSSLRequestedTransferAmount-1));
-            bytesWritten = -1;
-            bstd.mPRErrorCode = PR_WOULD_BLOCK_ERROR;
-            bstd.mOneBytePendingFromEarlierWrite = true;
-            bstd.mOriginalRequestedTransferAmount = bstd.mSSLRequestedTransferAmount;
-          }
-        }
-
-        bstd.mSSLResultRemainingBytes = bytesWritten;
-        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_writing_done;
-      }
-      else if (nsSSLSocketThreadData::ssl_pending_read == busy_socket_ssl_state)
-      {
-        PRInt32 bytesRead = realFileDesc->methods
-           ->read(realFileDesc, 
-                  bstd.mSSLDataBuffer, 
-                  bstd.mSSLRequestedTransferAmount);
-
-#ifdef DEBUG_SSL_VERBOSE
-        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)realFileDesc, bytesRead));
-#endif
-        bytesRead = checkHandshake(bytesRead, true, realFileDesc, mBusySocket);
-        if (bytesRead < 0) {
-          // give the error back to caller
-          bstd.mPRErrorCode = PR_GetError();
-        }
-
-        bstd.mSSLResultRemainingBytes = bytesRead;
-        bstd.mSSLRemainingReadResultData = bstd.mSSLDataBuffer;
-        busy_socket_ssl_state = nsSSLSocketThreadData::ssl_reading_done;
-      }
-    }
-
-    // avoid setting event repeatedly
-    bool needToSetPollableEvent = false;
-
-    {
-      MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-      
-      mBusySocket->mThreadData->mSSLState = busy_socket_ssl_state;
-      
-      if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-      {
-        needToSetPollableEvent = true;
-        nsSSLIOLayerHelpers::mPollableEventCurrentlySet = true;
-      }
-    }
-
-    if (needToSetPollableEvent && nsSSLIOLayerHelpers::mSharedPollableEvent)
-    {
-      // Wake up the file descriptor on the Necko thread,
-      // so it can fetch the results from the SSL I/O call 
-      // that we just completed.
-      PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-
-      // if we don't have a pollable event, we'll have to wake up
-      // the caller by other means.
-    }
-  }
-
-  {
-    MutexAutoLock threadLock(ssl_thread_singleton->mMutex);
-    if (mBusySocket)
-    {
-      restoreOriginalSocket_locked(mBusySocket);
-      mBusySocket = nsnull;
-    }
-    if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet)
-    {
-      nsSSLIOLayerHelpers::mPollableEventCurrentlySet = true;
-      if (nsSSLIOLayerHelpers::mSharedPollableEvent)
-      {
-        PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent);
-      }
-    }
-    postStoppedEventToMainThread(threadLock);
-  }
-}
-
-bool nsSSLThread::stoppedOrStopping()
-{
-  if (!ssl_thread_singleton)
-    return false;
-
-  return ssl_thread_singleton->exitRequestedNoLock();
-}
-
-nsSSLThread *nsSSLThread::ssl_thread_singleton = nsnull;
deleted file mode 100644
--- a/security/manager/ssl/src/nsSSLThread.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* ***** 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
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Kai Engert <kengert@redhat.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 _NSSSLTHREAD_H_
-#define _NSSSLTHREAD_H_
-
-#include "nsCOMPtr.h"
-#include "nsIRequest.h"
-#include "nsPSMBackgroundThread.h"
-
-class nsNSSSocketInfo;
-class nsIHttpChannel;
-
-class nsSSLThread : public nsPSMBackgroundThread
-{
-private:
-  // We use mMutex contained in our base class
-  // to protect access to these variables:
-  //   mBusySocket, mSocketScheduledToBeDestroyed
-  // and to nsSSLSocketThreadData::mSSLState
-  // while a socket is the busy socket.
-
-  // We use mCond contained in our base class
-  // to notify the SSL thread that a new SSL I/O 
-  // request has been queued for processing.
-  // It can be found in the mBusySocket variable,
-  // containing all details in its member.
-
-  // A socket that is currently owned by the SSL thread 
-  // and has pending SSL I/O activity or I/O results 
-  // not yet fetched by the original caller.
-  nsNSSSocketInfo *mBusySocket;
-  
-  // A socket that should be closed and destroyed
-  // as soon as possible. The request was initiated by
-  // Necko, but it happened at a time when the SSL
-  // thread had ownership of the socket, so the request
-  // was delayed. It's now the responsibility of the
-  // SSL thread to close and destroy this socket.
-  nsNSSSocketInfo *mSocketScheduledToBeDestroyed;
-
-  // Did we receive a request from NSS to fetch HTTP 
-  // data on behalf of NSS? (Most likely this is a OCSP request)
-  // We track a handle to the HTTP request sent to Necko.
-  // As this HTTP request depends on some original SSL socket,
-  // we can use this handle to cancel the dependent HTTP request,
-  // should we be asked to close the original SSL socket.
-  nsCOMPtr<nsIRequest> mPendingHTTPRequest;
-
-  virtual void Run(void);
-
-  // Called from SSL thread only
-  static PRInt32 checkHandshake(PRInt32 bytesTransfered, 
-                                bool wasReading,
-                                PRFileDesc* fd, 
-                                nsNSSSocketInfo *socketInfo);
-
-  // Function can be called from either Necko or SSL thread
-  // Caller must lock mMutex before this call.
-  static void restoreOriginalSocket_locked(nsNSSSocketInfo *si);
-
-  // Helper for requestSomething functions, 
-  // caled from the Necko thread only.
-  static PRFileDesc *getRealSSLFD(nsNSSSocketInfo *si);
-
-  // Support of blocking sockets is very rudimentary.
-  // We only support it because Mozilla's LDAP code requires blocking I/O.
-  // We do not support switching the blocking mode of a socket.
-  // We require the blocking state has been set prior to the first 
-  // read/write call, and will stay that way for the remainder of the socket's lifetime.
-  // This function must be called while holding the lock.
-  // If the socket is a blocking socket, out_fd will contain the real FD, 
-  // on a non-blocking socket out_fd will be nsnull.
-  // If there is a failure in obtaining the status of the socket,
-  // the function will return PR_FAILURE.
-  static PRStatus getRealFDIfBlockingSocket_locked(nsNSSSocketInfo *si, 
-                                                   PRFileDesc *&out_fd);
-public:
-  nsSSLThread();
-  ~nsSSLThread();
-
-  static nsSSLThread *ssl_thread_singleton;
-
-  // All requestSomething functions are called from 
-  // the Necko thread only.
-
-  static PRInt32 requestRead(nsNSSSocketInfo *si, 
-                             void *buf, 
-                             PRInt32 amount,
-                             PRIntervalTime timeout);
-
-  static PRInt32 requestWrite(nsNSSSocketInfo *si, 
-                              const void *buf, 
-                              PRInt32 amount,
-                              PRIntervalTime timeout);
-
-  static PRInt16 requestPoll(nsNSSSocketInfo *si, 
-                             PRInt16 in_flags, 
-                             PRInt16 *out_flags);
-
-  static PRInt32 requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount,
-                                    PRIntn flags, PRIntervalTime timeout);
-
-  static PRStatus requestClose(nsNSSSocketInfo *si);
-
-  static PRStatus requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr);
-
-  static PRStatus requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr);
-
-  static PRStatus requestGetsocketoption(nsNSSSocketInfo *si, 
-                                         PRSocketOptionData *data);
-
-  static PRStatus requestSetsocketoption(nsNSSSocketInfo *si, 
-                                         const PRSocketOptionData *data);
-
-  static PRStatus requestConnectcontinue(nsNSSSocketInfo *si, 
-                                         PRInt16 out_flags);
-
-  static nsresult requestActivateSSL(nsNSSSocketInfo *si);
-  
-  static bool stoppedOrStopping();
-};
-
-#endif //_NSSSLTHREAD_H_
--- a/security/nss/Makefile
+++ b/security/nss/Makefile
@@ -142,20 +142,20 @@ endif
 
 build_nspr: $(NSPR_CONFIG_STATUS)
 	cd $(CORE_DEPTH)/../nsprpub/$(OBJDIR_NAME) ; $(MAKE)
 
 clobber_nspr: $(NSPR_CONFIG_STATUS)
 	cd $(CORE_DEPTH)/../nsprpub/$(OBJDIR_NAME) ; $(MAKE) clobber
 
 build_dbm:
-ifndef NSS_DISABLE_DBM
+ifdef NSS_DISABLE_DBM
+	@echo "skipping the build of DBM"
+else
 	cd $(CORE_DEPTH)/dbm ; $(MAKE) export libs
-else
-	echo "skipping the build of DBM"
 endif
 
 clobber_dbm:
 	cd $(CORE_DEPTH)/dbm ; $(MAKE) clobber
 
 moz_import::
 ifeq (,$(filter-out WIN%,$(OS_TARGET)))
 	$(NSINSTALL) -D $(DIST)/include/nspr
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_13_1_RTM
+NSS_3_13_2_BETA1
--- a/security/nss/cmd/lib/pppolicy.c
+++ b/security/nss/cmd/lib/pppolicy.c
@@ -32,17 +32,17 @@
  * 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 ***** */
 
 /*
  * Support for various policy related extensions
  *
- * $Id: pppolicy.c,v 1.3 2005/02/22 20:02:22 wtchang%redhat.com Exp $
+ * $Id: pppolicy.c,v 1.5 2011/11/16 19:12:30 kaie%kuix.de Exp $
  */
 
 #include "seccomon.h"
 #include "secport.h"
 #include "secder.h"
 #include "cert.h"
 #include "secoid.h"
 #include "secasn1.h"
--- a/security/nss/cmd/ssltap/ssltap.c
+++ b/security/nss/cmd/ssltap/ssltap.c
@@ -61,17 +61,17 @@
 #include <string.h>
 #include <time.h>
 
 #include "plgetopt.h"
 #include "nss.h"
 #include "cert.h"
 #include "sslproto.h"
 
-#define VERSIONSTRING "$Revision: 1.19 $ ($Date: 2010/02/16 18:56:47 $) $Author: wtc%google.com $"
+#define VERSIONSTRING "$Revision: 1.20 $ ($Date: 2011/11/05 23:09:28 $) $Author: wtc%google.com $"
 
 
 struct _DataBufferList;
 struct _DataBuffer;
 
 typedef struct _DataBufferList {
   struct _DataBuffer *first,*last;
   int size;
@@ -1511,21 +1511,21 @@ showErr(const char * msg)
     errString = "(no text available)";
   PR_fprintf(PR_STDERR, "%s: Error %d: %s: %s", progName, err, errString, msg);
 }
 
 int main(int argc,  char *argv[])
 {
   char *hostname=NULL;
   PRUint16 rendport=DEFPORT,port;
-  PRHostEnt hp;
+  PRAddrInfo *ai;
+  void *iter;
   PRStatus r;
   PRNetAddr na_client,na_server,na_rend;
   PRFileDesc *s_server,*s_client,*s_rend; /*rendezvous */
-  char netdbbuf[PR_NETDB_BUF_SIZE];
   int c_count=0;
   PLOptState *optstate;
   PLOptStatus status;
   SECStatus   rv;
 
   progName = argv[0];
   optstate = PL_CreateOptState(argc,argv,"fvxhslp:");
     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
@@ -1586,24 +1586,24 @@ int main(int argc,  char *argv[])
 
   /* find the 'server' IP address so we don't have to look it up later */
 
   if (fancy) {
       PR_fprintf(PR_STDOUT,"<HTML><HEAD><TITLE>SSLTAP output</TITLE></HEAD>\n");
       PR_fprintf(PR_STDOUT,"<BODY><PRE>\n");
     }
   PR_fprintf(PR_STDERR,"Looking up \"%s\"...\n", hostname);
-  r = PR_GetHostByName(hostname,netdbbuf,PR_NETDB_BUF_SIZE,&hp);
-  if (r) {
+  ai = PR_GetAddrInfoByName(hostname, PR_AF_UNSPEC, PR_AI_ADDRCONFIG);
+  if (!ai) {
     showErr("Host Name lookup failed\n");
     exit(5);
   }
 
-  PR_EnumerateHostEnt(0,&hp,0,&na_server);
-  PR_InitializeNetAddr(PR_IpAddrNull,port,&na_server);
+  iter = NULL;
+  iter = PR_EnumerateAddrInfo(iter, ai, port, &na_server);
   /* set up the port which the client will connect to */
 
   r = PR_InitializeNetAddr(PR_IpAddrAny,rendport,&na_rend);
   if (r == PR_FAILURE) {
     PR_fprintf(PR_STDERR,
     "PR_InitializeNetAddr(,%d,) failed with error %d\n",PR_GetError());
     exit(0);
   }
@@ -1636,17 +1636,17 @@ int main(int argc,  char *argv[])
       PRPollDesc pds[2];
 
       s_client = PR_Accept(s_rend,&na_client,PR_SecondsToInterval(3600));
       if (s_client == NULL) {
 	showErr("accept timed out\n");
 	exit(7);
       }
 
-      s_server = PR_NewTCPSocket();
+      s_server = PR_OpenTCPSocket(na_server.raw.family);
       if (s_server == NULL) {
 	showErr("couldn't open new socket to connect to server \n");
 	exit(8);
       }
 
       r = PR_Connect(s_server,&na_server,PR_SecondsToInterval(5));
 
       if ( r == PR_FAILURE )
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -212,16 +212,18 @@ static void Usage(const char *progName)
                     "-n nickname");
     fprintf(stderr, 
             "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B");
     fprintf(stderr, "%-20s Disable SSL v2.\n", "-2");
     fprintf(stderr, "%-20s Disable SSL v3.\n", "-3");
     fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T");
     fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S");
     fprintf(stderr, "%-20s Client speaks first. \n", "-f");
+    fprintf(stderr, "%-20s Use synchronous certificate validation "
+                    "(required for SSL2)\n", "-O");
     fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o");
     fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s");
     fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
     fprintf(stderr, "%-20s Use export policy.\n", "-x");
     fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
     fprintf(stderr, "%-20s Enable compression.\n", "-z");
@@ -288,30 +290,54 @@ disableAllSSLCiphers(void)
 	    fprintf(stderr,
 	            "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n",
 	    	   suite, i, SECU_Strerror(err));
 	    exit(2);
 	}
     }
 }
 
+typedef struct
+{
+   PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert 
+                        * authentication */
+   PRBool isPaused;    /* PR_TRUE if libssl is waiting for us to validate the
+                        * peer's certificate and restart the handshake. */
+   void * dbHandle;    /* Certificate database handle to use while
+                        * authenticating the peer's certificate. */
+} ServerCertAuth;
+
 /*
  * Callback is called when incoming certificate is not valid.
  * Returns SECSuccess to accept the cert anyway, SECFailure to reject.
  */
 static SECStatus 
 ownBadCertHandler(void * arg, PRFileDesc * socket)
 {
     PRErrorCode err = PR_GetError();
     /* can log invalid cert here */
     fprintf(stderr, "Bad server certificate: %d, %s\n", err, 
             SECU_Strerror(err));
     return SECSuccess;	/* override, say it's OK. */
 }
 
+static SECStatus 
+ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
+                       PRBool isServer)
+{
+    ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg;
+
+    FPRINTF(stderr, "using asynchronous certificate validation\n", progName);
+
+    PORT_Assert(serverCertAuth->shouldPause);
+    PORT_Assert(!serverCertAuth->isPaused);
+    serverCertAuth->isPaused = PR_TRUE;
+    return SECWouldBlock;
+}
+
 SECStatus
 own_GetClientAuthData(void *                       arg,
                       PRFileDesc *                 socket,
                       struct CERTDistNamesStr *    caNames,
                       struct CERTCertificateStr ** pRetCert,
                       struct SECKEYPrivateKeyStr **pRetKey)
 {
     if (verbose > 1) {
@@ -493,21 +519,47 @@ separateReqHeader(const PRFileDesc* outF
     } else if (((c) >= 'a') && ((c) <= 'f')) { \
 	i = (c) - 'a' + 10; \
     } else if (((c) >= 'A') && ((c) <= 'F')) { \
 	i = (c) - 'A' + 10; \
     } else { \
 	Usage(progName); \
     }
 
+static SECStatus
+restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
+                                        ServerCertAuth * serverCertAuth,
+                                        PRBool override)
+{
+    SECStatus rv;
+    
+    if (!serverCertAuth->isPaused)
+	return SECSuccess;
+    
+    FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n",
+            progName);
+
+    serverCertAuth->isPaused = PR_FALSE;
+    rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE);
+    if (rv != SECSuccess && override) {
+        rv = ownBadCertHandler(NULL, fd);
+    }
+    if (rv != SECSuccess) {
+	return rv;
+    }
+    
+    rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
+
+    return rv;
+}
+    
 int main(int argc, char **argv)
 {
     PRFileDesc *       s;
     PRFileDesc *       std_out;
-    CERTCertDBHandle * handle;
     char *             host	=  NULL;
     char *             certDir  =  NULL;
     char *             nickname =  NULL;
     char *             cipherString = NULL;
     char *             tmp;
     int                multiplier = 0;
     SECStatus          rv;
     PRStatus           status;
@@ -525,51 +577,58 @@ int main(int argc, char **argv)
     int                enableFalseStart = 0;
     PRSocketOptionData opt;
     PRNetAddr          addr;
     PRPollDesc         pollset[2];
     PRBool             pingServerFirst = PR_FALSE;
     PRBool             clientSpeaksFirst = PR_FALSE;
     PRBool             wrStarted = PR_FALSE;
     PRBool             skipProtoHeader = PR_FALSE;
+    ServerCertAuth     serverCertAuth;
     int                headerSeparatorPtrnId = 0;
     int                error = 0;
     PRUint16           portno = 443;
     char *             hs1SniHostName = NULL;
     char *             hs2SniHostName = NULL;
     PLOptState *optstate;
     PLOptStatus optstatus;
     PRStatus prStatus;
 
+    serverCertAuth.shouldPause = PR_TRUE;
+    serverCertAuth.isPaused = PR_FALSE;
+    serverCertAuth.dbHandle = NULL;
+
     progName = strrchr(argv[0], '/');
     if (!progName)
 	progName = strrchr(argv[0], '\\');
     progName = progName ? progName+1 : argv[0];
 
     tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT");
     if (tmp && tmp[0]) {
        int sec = PORT_Atoi(tmp);
        if (sec > 0) {
            maxInterval = PR_SecondsToInterval(sec);
        }
     }
 
     optstate = PL_CreateOptState(argc, argv,
-                                 "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
+                                 "23BOSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
 	switch (optstate->option) {
 	  case '?':
 	  default : Usage(progName); 			break;
 
           case '2': disableSSL2 = 1; 			break;
 
           case '3': disableSSL3 = 1; 			break;
 
           case 'B': bypassPKCS11 = 1; 			break;
 
+          case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
+
           case 'S': skipProtoHeader = PR_TRUE;                 break;
 
           case 'T': disableTLS  = 1; 			break;
 
           case 'a': if (!hs1SniHostName) {
                         hs1SniHostName = PORT_Strdup(optstate->value);
                     } else if (!hs2SniHostName) {
                         hs2SniHostName =  PORT_Strdup(optstate->value);
@@ -645,24 +704,18 @@ int main(int argc, char **argv)
     } else {
 	char *certDirTmp = certDir;
 	certDir = SECU_ConfigDirectory(certDirTmp);
 	PORT_Free(certDirTmp);
     }
     rv = NSS_Init(certDir);
     if (rv != SECSuccess) {
 	SECU_PrintError(progName, "unable to open cert database");
-#if 0
-    rv = CERT_OpenVolatileCertDB(handle);
-	CERT_SetDefaultCertDB(handle);
-#else
 	return 1;
-#endif
     }
-    handle = CERT_GetDefaultCertDB();
 
     /* set the policy bits true for all the cipher suites. */
     if (useExportPolicy)
 	NSS_SetExportPolicy();
     else
 	NSS_SetDomesticPolicy();
 
     /* all the SSL2 and SSL3 cipher suites are enabled by default. */
@@ -871,17 +924,23 @@ int main(int argc, char **argv)
     rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
     if (rv != SECSuccess) {
 	SECU_PrintError(progName, "error enabling false start");
 	return 1;
     }
 
     SSL_SetPKCS11PinArg(s, &pwdata);
 
-    SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
+    serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
+
+    if (serverCertAuth.shouldPause) {
+	SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
+    } else {
+	SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle);
+    }
     if (override) {
 	SSL_BadCertHook(s, ownBadCertHandler, NULL);
     }
     SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
     SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
     if (hs1SniHostName) {
         SSL_SetURL(s, hs1SniHostName);
     } else {
@@ -979,16 +1038,24 @@ int main(int argc, char **argv)
     ** socket, read data from socket and write to stdout.
     */
     FPRINTF(stderr, "%s: ready...\n", progName);
 
     while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) {
 	char buf[4000];	/* buffer for stdin */
 	int nb;		/* num bytes read from stdin. */
 
+	rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
+						     override);
+	if (rv != SECSuccess) {
+	    error = 254; /* 254 (usually) means "handshake failed" */
+	    SECU_PrintError(progName, "authentication of server cert failed");
+	    goto done;
+	}
+	        
 	pollset[SSOCK_FD].out_flags = 0;
 	pollset[STDIN_FD].out_flags = 0;
 
 	FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
 	filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
 	if (filesReady < 0) {
 	    SECU_PrintError(progName, "select failed");
 	    error = 1;
@@ -1037,16 +1104,25 @@ int main(int argc, char **argv)
 			    goto done;
 			}
 			cc = 0;
 		    }
 		    bufp += cc;
 		    nb   -= cc;
 		    if (nb <= 0) 
 		    	break;
+
+		    rv = restartHandshakeAfterServerCertIfNeeded(s,
+				&serverCertAuth, override);
+		    if (rv != SECSuccess) {
+			error = 254; /* 254 (usually) means "handshake failed" */
+			SECU_PrintError(progName, "authentication of server cert failed");
+			goto done;
+		    }
+
 		    pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
 		    pollset[SSOCK_FD].out_flags = 0;
 		    FPRINTF(stderr,
 		            "%s: about to call PR_Poll on writable socket !\n", 
 			    progName);
 		    cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
 		    FPRINTF(stderr,
 		            "%s: PR_Poll returned with writable socket !\n", 
--- a/security/nss/lib/certdb/cert.h
+++ b/security/nss/lib/certdb/cert.h
@@ -32,17 +32,17 @@
  * 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 ***** */
 
 /*
  * cert.h - public data structures and prototypes for the certificate library
  *
- * $Id: cert.h,v 1.86 2011/07/24 13:48:09 wtc%google.com Exp $
+ * $Id: cert.h,v 1.88 2011/11/16 19:12:32 kaie%kuix.de Exp $
  */
 
 #ifndef _CERT_H_
 #define _CERT_H_
 
 #include "utilrename.h"
 #include "plarena.h"
 #include "plhash.h"
--- a/security/nss/lib/certdb/certdb.c
+++ b/security/nss/lib/certdb/certdb.c
@@ -34,17 +34,17 @@
  * 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 ***** */
 
 /*
  * Certificate handling code
  *
- * $Id: certdb.c,v 1.116 2011/08/05 01:13:14 wtc%google.com Exp $
+ * $Id: certdb.c,v 1.120 2011/11/17 00:20:20 bsmith%mozilla.com Exp $
  */
 
 #include "nssilock.h"
 #include "prmon.h"
 #include "prtime.h"
 #include "cert.h"
 #include "certi.h"
 #include "secder.h"
@@ -591,27 +591,16 @@ cert_ComputeCertType(CERTCertificate *ce
 	    SECSuccess){
 	    if (basicConstraintPresent == PR_TRUE &&
 		(basicConstraint.isCA)) {
 		nsCertType |= NS_CERT_TYPE_SSL_CA;
 	    } else {
 		nsCertType |= NS_CERT_TYPE_SSL_SERVER;
 	    }
 	}
-	/* Treat certs with step-up OID as also having SSL server type. */
-	if (findOIDinOIDSeqByTagNum(extKeyUsage, 
-				    SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
-	    SECSuccess){
-	    if (basicConstraintPresent == PR_TRUE &&
-		(basicConstraint.isCA)) {
-		nsCertType |= NS_CERT_TYPE_SSL_CA;
-	    } else {
-		nsCertType |= NS_CERT_TYPE_SSL_SERVER;
-	    }
-	}
 	if (findOIDinOIDSeqByTagNum(extKeyUsage,
 				    SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) ==
 	    SECSuccess){
 	    if (basicConstraintPresent == PR_TRUE &&
 		(basicConstraint.isCA)) {
 		nsCertType |= NS_CERT_TYPE_SSL_CA;
 	    } else {
 		nsCertType |= NS_CERT_TYPE_SSL_CLIENT;
--- a/security/nss/lib/certdb/certv3.c
+++ b/security/nss/lib/certdb/certv3.c
@@ -32,17 +32,17 @@
  * 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 ***** */
 
 /*
  * Code for dealing with X509.V3 extensions.
  *
- * $Id: certv3.c,v 1.10 2007/10/12 01:44:40 julien.pierre.boogz%sun.com Exp $
+ * $Id: certv3.c,v 1.12 2011/11/16 19:12:32 kaie%kuix.de Exp $
  */
 
 #include "cert.h"
 #include "secitem.h"
 #include "secoid.h"
 #include "secder.h"
 #include "secasn1.h"
 #include "certxutl.h"
--- a/security/nss/lib/certdb/polcyxtn.c
+++ b/security/nss/lib/certdb/polcyxtn.c
@@ -32,17 +32,17 @@
  * 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 ***** */
 
 /*
  * Support for various policy related extensions
  *
- * $Id: polcyxtn.c,v 1.11 2008/02/13 04:03:19 julien.pierre.boogz%sun.com Exp $
+ * $Id: polcyxtn.c,v 1.13 2011/11/16 19:12:32 kaie%kuix.de Exp $
  */
 
 #include "seccomon.h"
 #include "secport.h"
 #include "secder.h"
 #include "cert.h"
 #include "secoid.h"
 #include "secasn1.h"
--- a/security/nss/lib/certhigh/certvfypkix.c
+++ b/security/nss/lib/certhigh/certvfypkix.c
@@ -220,32 +220,27 @@ enum {
 typedef struct {
     SECCertUsage certUsage;
     PRUint32 ekuStringIndex;
 } SECCertUsageToEku;
 
 const SECCertUsageToEku certUsageEkuStringMap[] = {
     {certUsageSSLClient,             ekuIndexSSLClient},
     {certUsageSSLServer,             ekuIndexSSLServer},
-    {certUsageSSLServerWithStepUp,   ekuIndexSSLServer}, /* need to add oids to
-                                                          * the list of eku.
-                                                          * see 390381*/
     {certUsageSSLCA,                 ekuIndexSSLServer},
     {certUsageEmailSigner,           ekuIndexEmail},
     {certUsageEmailRecipient,        ekuIndexEmail},
     {certUsageObjectSigner,          ekuIndexCodeSigner},
     {certUsageUserCertImport,        ekuIndexUnknown},
     {certUsageVerifyCA,              ekuIndexUnknown},
     {certUsageProtectedObjectSigner, ekuIndexUnknown},
     {certUsageStatusResponder,       ekuIndexStatusResponder},
     {certUsageAnyCA,                 ekuIndexUnknown},
 };
 
-#define CERT_USAGE_EKU_STRING_MAPS_TOTAL       12
-
 /*
  * FUNCTION: cert_NssCertificateUsageToPkixKUAndEKU
  * DESCRIPTION:
  *
  * Converts nss CERTCertificateUsage bit field to pkix key and
  * extended key usages.
  *
  * PARAMETERS:
@@ -287,17 +282,17 @@ cert_NssCertificateUsageToPkixKUAndEKU(
 
     PKIX_ENTER(CERTVFYPKIX, "cert_NssCertificateUsageToPkixEku");
     PKIX_NULLCHECK_TWO(ppkixEKUList, ppkixKU);
     
     PKIX_CHECK(
         PKIX_List_Create(&ekuOidsList, plContext),
         PKIX_LISTCREATEFAILED);
 
-    for (;i < CERT_USAGE_EKU_STRING_MAPS_TOTAL;i++) {
+    for (;i < PR_ARRAY_SIZE(certUsageEkuStringMap);i++) {
         const SECCertUsageToEku *usageToEkuElem =
             &certUsageEkuStringMap[i];
         if (usageToEkuElem->certUsage == requiredCertUsage) {
             ekuIndex = usageToEkuElem->ekuStringIndex;
             break;
         }
     }
     if (ekuIndex != ekuIndexUnknown) {
--- a/security/nss/lib/ckfw/builtins/certdata.c
+++ b/security/nss/lib/ckfw/builtins/certdata.c
@@ -30,17 +30,17 @@
  * 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 ***** */
 #ifdef DEBUG
-static const char CVS_ID[] = "@(#) $RCSfile: certdata.txt,v $ $Revision: 1.79 $ $Date: 2011/09/02 19:40:56 $""; @(#) $RCSfile: certdata.perl,v $ $Revision: 1.13 $ $Date: 2010/03/26 22:06:47 $";
+static const char CVS_ID[] = "@(#) $RCSfile: certdata.c,v $ $Revision: 1.83 $ $Date: 2011/11/03 15:11:57 $""; @(#) $RCSfile: certdata.c,v $ $Revision: 1.83 $ $Date: 2011/11/03 15:11:57 $";
 #endif /* DEBUG */
 
 #ifndef BUILTINS_H
 #include "builtins.h"
 #endif /* BUILTINS_H */
 
 static const CK_BBOOL ck_false = CK_FALSE;
 static const CK_BBOOL ck_true = CK_TRUE;
@@ -1090,17 +1090,17 @@ static const CK_ATTRIBUTE_TYPE nss_built
 #ifdef DEBUG
 static const NSSItem nss_builtins_items_0 [] = {
   { (void *)&cko_data, (PRUint32)sizeof(CK_OBJECT_CLASS) },
   { (void *)&ck_true, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)"CVS ID", (PRUint32)7 },
   { (void *)"NSS", (PRUint32)4 },
-  { (void *)"@(#) $RCSfile: certdata.txt,v $ $Revision: 1.79 $ $Date: 2011/09/02 19:40:56 $""; @(#) $RCSfile: certdata.perl,v $ $Revision: 1.13 $ $Date: 2010/03/26 22:06:47 $", (PRUint32)160 }
+  { (void *)"@(#) $RCSfile: certdata.c,v $ $Revision: 1.83 $ $Date: 2011/11/03 15:11:57 $""; @(#) $RCSfile: certdata.c,v $ $Revision: 1.83 $ $Date: 2011/11/03 15:11:57 $", (PRUint32)160 }
 };
 #endif /* DEBUG */
 static const NSSItem nss_builtins_items_1 [] = {
   { (void *)&cko_nss_builtin_root_list, (PRUint32)sizeof(CK_OBJECT_CLASS) },
   { (void *)&ck_true, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) },
   { (void *)"Mozilla Builtin Roots", (PRUint32)22 }
--- a/security/nss/lib/ckfw/builtins/certdata.txt
+++ b/security/nss/lib/ckfw/builtins/certdata.txt
@@ -29,17 +29,17 @@
 # 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 *****
-CVS_ID "@(#) $RCSfile: certdata.txt,v $ $Revision: 1.79 $ $Date: 2011/09/02 19:40:56 $"
+CVS_ID "@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $"
 
 #
 # certdata.txt
 #
 # This file contains the object definitions for the certs and other
 # information "built into" NSS.
 #
 # Object definitions:
--- a/security/nss/lib/cryptohi/keyhi.h
+++ b/security/nss/lib/cryptohi/keyhi.h
@@ -30,17 +30,17 @@
  * 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 ***** */
-/* $Id: keyhi.h,v 1.18 2011/07/24 13:48:12 wtc%google.com Exp $ */
+/* $Id: keyhi.h,v 1.20 2011/11/16 19:12:33 kaie%kuix.de Exp $ */
 
 #ifndef _KEYHI_H_
 #define _KEYHI_H_
 
 #include "plarena.h"
 
 #include "seccomon.h"
 #include "secoidt.h"
--- a/security/nss/lib/freebl/blapi.h
+++ b/security/nss/lib/freebl/blapi.h
@@ -32,17 +32,17 @@
  * 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 ***** */
-/* $Id: blapi.h,v 1.42 2011/10/04 22:05:53 wtc%google.com Exp $ */
+/* $Id: blapi.h,v 1.43 2011/10/29 23:28:45 wtc%google.com Exp $ */
 
 #ifndef _BLAPI_H_
 #define _BLAPI_H_
 
 #include "blapit.h"
 #include "hasht.h"
 #include "alghmac.h"
 
@@ -268,17 +268,17 @@ JPAKE_Sign(PLArenaPool * arena, const PQ
            SECItem * gv, SECItem * r);
 
 /* Given gx == g^x, verify the Schnorr zero-knowledge proof (gv, r) for the
  * value x using the specified hash algorithm and signer ID.
  *
  * The arena is *not* optional so do not pass NULL for the arena parameter. 
  */
 SECStatus
-JPAKE_Verify(PRArenaPool * arena, const PQGParams * pqg,
+JPAKE_Verify(PLArenaPool * arena, const PQGParams * pqg,
              HASH_HashType hashType, const SECItem * signerID,
              const SECItem * peerID, const SECItem * gx,
              const SECItem * gv, const SECItem * r);
 
 /* Call before round 2 with x2, s, and x2s all non-NULL. This will calculate
  * base = g^(x1+x3+x4) (mod p) and x2s = x2*s (mod q). The values to send in 
  * round 2 (A and the proof of knowledge of x2s) can then be calculated with
  * JPAKE_Sign using pqg->base = base and x = x2s.
--- a/security/nss/lib/freebl/jpake.c
+++ b/security/nss/lib/freebl/jpake.c
@@ -217,17 +217,17 @@ cleanup:
         MP_TO_SEC_ERROR(err);
         rv = SECFailure;
     }
     return rv;
 }
 
 /* Verify a Schnorr signature generated by the peer in round 1 or round 2. */
 SECStatus
-JPAKE_Verify(PRArenaPool * arena, const PQGParams * pqg, HASH_HashType hashType,
+JPAKE_Verify(PLArenaPool * arena, const PQGParams * pqg, HASH_HashType hashType,
              const SECItem * signerID, const SECItem * peerID,
              const SECItem * gx, const SECItem * gv, const SECItem * r)
 {
     SECStatus rv = SECSuccess;
     mp_err err;
     mp_int p;
     mp_int q;
     mp_int g;
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1023,8 +1023,14 @@ CERT_DestroyCERTRevocationFlags;
 ;+NSS_3.13 { 	# NSS 3.13 release
 ;+    global:
 ;;SECKEY_RSAPSSParamsTemplate DATA ;
 NSS_Get_SECKEY_RSAPSSParamsTemplate;
 NSS_GetVersion;
 ;+    local:
 ;+       *;
 ;+};
+;+NSS_3.13.2 { 	# NSS 3.13.2 release
+;+    global:
+PK11_ImportEncryptedPrivateKeyInfoAndReturnKey;
+;+    local:
+;+       *;
+;+};
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -31,17 +31,17 @@
  * 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 ***** */
-/* $Id: nss.h,v 1.86 2011/10/27 19:29:44 kaie%kuix.de Exp $ */
+/* $Id: nss.h,v 1.87 2011/10/27 19:39:00 kaie%kuix.de Exp $ */
 
 #ifndef __nss_h_
 #define __nss_h_
 
 /* The private macro _NSS_ECC_STRING is for NSS internal use only. */
 #ifdef NSS_ENABLE_ECC
 #ifdef NSS_ECC_MORE_THAN_SUITE_B
 #define _NSS_ECC_STRING " Extended ECC"
@@ -61,22 +61,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION  "3.13.1.0" _NSS_ECC_STRING _NSS_CUSTOMIZED
+#define NSS_VERSION  "3.13.2.0" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR   3
 #define NSS_VMINOR   13
-#define NSS_VPATCH   1
+#define NSS_VPATCH   2
 #define NSS_VBUILD   0
-#define NSS_BETA     PR_FALSE
+#define NSS_BETA     PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/pk11wrap/pk11akey.c
+++ b/security/nss/lib/pk11wrap/pk11akey.c
@@ -1569,23 +1569,46 @@ PK11_MakeKEAPubKey(unsigned char *keyDat
     rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData);
     if (rv != SECSuccess) {
 	PORT_FreeArena (arena, PR_FALSE);
 	return NULL;
     }
     return pubk;
 }
 
+/*
+ * NOTE: This function doesn't return a SECKEYPrivateKey struct to represent
+ * the new private key object.  If it were to create a session object that
+ * could later be looked up by its nickname, it would leak a SECKEYPrivateKey.
+ * So isPerm must be true.
+ */
 SECStatus 
 PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
 			SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
 			SECItem *nickname, SECItem *publicValue, PRBool isPerm,
 			PRBool isPrivate, KeyType keyType, 
 			unsigned int keyUsage, void *wincx)
 {
+    if (!isPerm) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+    return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(slot, epki,
+		pwitem, nickname, publicValue, isPerm, isPrivate, keyType,
+		keyUsage, NULL, wincx);
+}
+
+SECStatus
+PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot,
+			SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+			SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+			PRBool isPrivate, KeyType keyType,
+			unsigned int keyUsage, SECKEYPrivateKey **privk,
+			void *wincx)
+{
     CK_MECHANISM_TYPE pbeMechType;
     SECItem *crypto_param = NULL;
     PK11SymKey *key = NULL;
     SECStatus rv = SECSuccess;
     CK_MECHANISM_TYPE cryptoMechType;
     SECKEYPrivateKey *privKey = NULL;
     PRBool faulty3DES = PR_FALSE;
     int usageCount = 0;
@@ -1671,17 +1694,21 @@ try_faulty_3des:
 
     PORT_Assert(usage != NULL);
     PORT_Assert(usageCount != 0);
     privKey = PK11_UnwrapPrivKey(slot, key, cryptoMechType, 
 				 crypto_param, &epki->encryptedData, 
 				 nickname, publicValue, isPerm, isPrivate,
 				 key_type, usage, usageCount, wincx);
     if(privKey) {
-	SECKEY_DestroyPrivateKey(privKey);
+	if (privk) {
+	    *privk = privKey;
+	} else {
+	    SECKEY_DestroyPrivateKey(privKey);
+	}
 	privKey = NULL;
 	rv = SECSuccess;
 	goto done;
     }
 
     /* if we are unable to import the key and the pbeMechType is 
      * CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that
      * the encrypted blob was created with a buggy key generation method
--- a/security/nss/lib/pk11wrap/pk11pub.h
+++ b/security/nss/lib/pk11wrap/pk11pub.h
@@ -566,16 +566,21 @@ SECStatus PK11_ImportDERPrivateKeyInfoAn
 		SECItem *derPKI, SECItem *nickname,
 		SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
 		unsigned int usage, SECKEYPrivateKey** privk, void *wincx);
 SECStatus PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, 
 		SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem, 
 		SECItem *nickname, SECItem *publicValue, PRBool isPerm,
 		PRBool isPrivate, KeyType type, 
 		unsigned int usage, void *wincx);
+SECStatus PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, 
+		SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem, 
+		SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+		PRBool isPrivate, KeyType type, 
+		unsigned int usage, SECKEYPrivateKey** privk, void *wincx);
 SECKEYPrivateKeyInfo *PK11_ExportPrivateKeyInfo(
 		CERTCertificate *cert, void *wincx);
 SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivKeyInfo(
 		PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
 		SECKEYPrivateKey *pk, int iteration, void *wincx);
 SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo(
 		PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
 		CERTCertificate *cert, int iteration, void *wincx);
--- a/security/nss/lib/pkcs7/p7decode.c
+++ b/security/nss/lib/pkcs7/p7decode.c
@@ -33,17 +33,17 @@
  * 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 ***** */
 
 /*
  * PKCS7 decoding, verification.
  *
- * $Id: p7decode.c,v 1.26 2011/08/21 01:14:17 wtc%google.com Exp $
+ * $Id: p7decode.c,v 1.28 2011/11/16 19:12:34 kaie%kuix.de Exp $
  */
 
 #include "p7local.h"
 
 #include "cert.h"
 				/* XXX do not want to have to include */
 #include "certdb.h"		/* certdb.h -- the trust stuff needed by */
      				/* the add certificate code needs to get */
--- a/security/nss/lib/pkcs7/secpkcs7.h
+++ b/security/nss/lib/pkcs7/secpkcs7.h
@@ -32,17 +32,17 @@
  * 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 ***** */
 
 /*
  * Interface to the PKCS7 implementation.
  *
- * $Id: secpkcs7.h,v 1.6 2008/06/14 14:20:25 wtc%google.com Exp $
+ * $Id: secpkcs7.h,v 1.8 2011/11/16 19:12:34 kaie%kuix.de Exp $
  */
 
 #ifndef _SECPKCS7_H_
 #define _SECPKCS7_H_
 
 #include "seccomon.h"
 
 #include "secoidt.h"
--- a/security/nss/lib/pki/pki3hack.c
+++ b/security/nss/lib/pki/pki3hack.c
@@ -30,17 +30,17 @@
  * 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 ***** */
 
 #ifdef DEBUG
-static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.102 $ $Date: 2011/04/13 00:10:26 $";
+static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.105 $ $Date: 2011/11/17 00:20:21 $";
 #endif /* DEBUG */
 
 /*
  * Hacks to integrate NSS 3.4 and NSS 4.0 certificates.
  */
 
 #ifndef NSSPKI_H
 #include "nsspki.h"
@@ -587,20 +587,16 @@ cert_trust_from_stan_trust(NSSTrust *t, 
     client = get_nss3trust_from_nss4trust(t->clientAuth);
     if (client & (CERTDB_TRUSTED_CA|CERTDB_NS_TRUSTED_CA)) {
 	client &= ~(CERTDB_TRUSTED_CA|CERTDB_NS_TRUSTED_CA);
 	rvTrust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA;
     }
     rvTrust->sslFlags |= client;
     rvTrust->emailFlags = get_nss3trust_from_nss4trust(t->emailProtection);
     rvTrust->objectSigningFlags = get_nss3trust_from_nss4trust(t->codeSigning);
-    /* The cert is a valid step-up cert (in addition to/lieu of trust above */
-    if (t->stepUpApproved) {
-	rvTrust->sslFlags |= CERTDB_GOVT_APPROVED_CA;
-    }
     return rvTrust;
 }
 
 CERTCertTrust * 
 nssTrust_GetCERTCertTrustForCert(NSSCertificate *c, CERTCertificate *cc)
 {
     CERTCertTrust *rvTrust = NULL;
     NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
--- a/security/nss/lib/softoken/jpakesftk.c
+++ b/security/nss/lib/softoken/jpakesftk.c
@@ -69,19 +69,19 @@ jpake_Sign(PLArenaPool * arena, const PQ
     
     gx.data = NULL;
     gv.data = NULL;
     r.data = NULL;
     crv = jpake_mapStatus(JPAKE_Sign(arena, pqg, hashType, signerID, x, NULL,
                                      NULL, &gx, &gv, &r),
                           CKR_MECHANISM_PARAM_INVALID);
     if (crv == CKR_OK) {
-        if (out->pGX != NULL && out->ulGXLen >= gx.len ||
-            out->pGV != NULL && out->ulGVLen >= gv.len ||
-            out->pR  != NULL && out->ulRLen >= r.len) {
+        if ((out->pGX != NULL && out->ulGXLen >= gx.len) ||
+            (out->pGV != NULL && out->ulGVLen >= gv.len) ||
+            (out->pR  != NULL && out->ulRLen >= r.len)) {
             PORT_Memcpy(out->pGX, gx.data, gx.len); 
             PORT_Memcpy(out->pGV, gv.data, gv.len); 
             PORT_Memcpy(out->pR, r.data, r.len);
             out->ulGXLen = gx.len;
             out->ulGVLen = gv.len;
             out->ulRLen = r.len;
         } else {
             crv = CKR_MECHANISM_PARAM_INVALID;
@@ -103,31 +103,16 @@ jpake_Verify(PLArenaPool * arena, const 
     r.data = publicValueIn->pR;   r.len = publicValueIn->ulRLen;
     return jpake_mapStatus(JPAKE_Verify(arena, pqg, hashType, signerID, &peerID,
                                         &gx, &gv, &r),
                            CKR_MECHANISM_PARAM_INVALID);
 }
 
 #define NUM_ELEM(x) (sizeof (x) / sizeof (x)[0])
 
-/* Ensure that the key is of the given type. */
-static CK_RV
-jpake_ensureKeyType(SFTKObject * key, CK_KEY_TYPE keyType)
-{
-    CK_RV crv;
-    SFTKAttribute * keyTypeAttr = sftk_FindAttribute(key, CKA_KEY_TYPE);
-    crv = keyTypeAttr != NULL && 
-          *(CK_KEY_TYPE *)keyTypeAttr->attrib.pValue == keyType
-        ? CKR_OK
-        : CKR_TEMPLATE_INCONSISTENT;
-    if (keyTypeAttr != NULL)
-        sftk_FreeAttribute(keyTypeAttr);
-    return crv;
-}
-
 /* If the template has the key type set, ensure that it was set to the correct
  * value. If the template did not have the key type set, set it to the
  * correct value.
  */
 static CK_RV
 jpake_enforceKeyType(SFTKObject * key, CK_KEY_TYPE keyType) {
     CK_RV crv;
     SFTKAttribute * keyTypeAttr = sftk_FindAttribute(key, CKA_KEY_TYPE);
--- a/security/nss/lib/softoken/legacydb/config.mk
+++ b/security/nss/lib/softoken/legacydb/config.mk
@@ -33,21 +33,20 @@
 # 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 *****
 
 # $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
 CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
 
-EXTRA_LIBS +=	$(CRYPTOLIB) 
-
-ifndef NSS_DISABLE_DBM
-EXTRA_LIBS +=	$(DIST)/lib/$(LIB_PREFIX)dbm.$(LIB_SUFFIX) 
-endif
+EXTRA_LIBS += \
+	$(CRYPTOLIB) \
+	$(DIST)/lib/$(LIB_PREFIX)dbm.$(LIB_SUFFIX) \
+	$(NULL)
 
 # can't do this in manifest.mn because OS_TARGET isn't defined there.
 ifeq (,$(filter-out WIN%,$(OS_TARGET)))
 
 # don't want the 32 in the shared library name
 SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
 IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
 
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -52,16 +52,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION  "3.13.1.0" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION  "3.13.2.0" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR   3
 #define SOFTOKEN_VMINOR   13
-#define SOFTOKEN_VPATCH   1
+#define SOFTOKEN_VPATCH   2
 #define SOFTOKEN_VBUILD   0
-#define SOFTOKEN_BETA     PR_FALSE
+#define SOFTOKEN_BETA     PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/SSLerrs.h
+++ b/security/nss/lib/ssl/SSLerrs.h
@@ -400,8 +400,20 @@ ER3(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED,
 ER3(SSL_ERROR_UNSAFE_NEGOTIATION,              (SSL_ERROR_BASE + 113),
 "Peer attempted old style (potentially vulnerable) handshake.")
 
 ER3(SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD, (SSL_ERROR_BASE + 114),
 "SSL received an unexpected uncompressed record.")
 
 ER3(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY,    (SSL_ERROR_BASE + 115),
 "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.")
+
+ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID,      (SSL_ERROR_BASE + 116),
+"SSL received invalid NPN extension data.")
+
+ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2,  (SSL_ERROR_BASE + 117),
+"SSL feature not supported for SSL 2.0 connections.")
+
+ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS, (SSL_ERROR_BASE + 118),
+"SSL feature not supported for servers.")
+
+ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS, (SSL_ERROR_BASE + 119),
+"SSL feature not supported for clients.")
--- a/security/nss/lib/ssl/ssl.def
+++ b/security/nss/lib/ssl/ssl.def
@@ -159,8 +159,17 @@ SSL_ConfigSecureServerWithCertChain;
 ;+*;
 ;+};
 ;+NSS_3.13 {    # NSS 3.13 release
 ;+    global:
 NSSSSL_GetVersion;
 ;+    local:
 ;+       *;
 ;+};
+;+NSS_3.13.2 {    # NSS 3.13.2 release
+;+    global:
+SSL_SetNextProtoCallback;
+SSL_SetNextProtoNego;
+SSL_GetNextProto;
+SSL_RestartHandshakeAfterAuthCertificate;
+;+    local:
+;+       *;
+;+};
--- a/security/nss/lib/ssl/ssl.h
+++ b/security/nss/lib/ssl/ssl.h
@@ -31,17 +31,17 @@
  * 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 ***** */
-/* $Id: ssl.h,v 1.44 2011/10/06 22:42:33 wtc%google.com Exp $ */
+/* $Id: ssl.h,v 1.45 2011/10/29 00:29:11 bsmith%mozilla.com Exp $ */
 
 #ifndef __ssl_h_
 #define __ssl_h_
 
 #include "prtypes.h"
 #include "prerror.h"
 #include "prio.h"
 #include "seccomon.h"
@@ -176,16 +176,72 @@ SSL_IMPORT SECStatus SSL_EnableDefault(i
 
 /* New function names */
 SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRBool on);
 SSL_IMPORT SECStatus SSL_OptionGet(PRFileDesc *fd, PRInt32 option, PRBool *on);
 SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on);
 SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on);
 SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle);
 
+/* SSLNextProtoCallback is called during the handshake for the client, when a
+ * Next Protocol Negotiation (NPN) extension has been received from the server.
+ * |protos| and |protosLen| define a buffer which contains the server's
+ * advertisement. This data is guaranteed to be well formed per the NPN spec.
+ * |protoOut| is a buffer provided by the caller, of length 255 (the maximum
+ * allowed by the protocol). On successful return, the protocol to be announced
+ * to the server will be in |protoOut| and its length in |*protoOutLen|.
+ *
+ * The callback must return SECFailure or SECSuccess (not SECWouldBlock).
+ */
+typedef SECStatus (PR_CALLBACK *SSLNextProtoCallback)(
+    void *arg,
+    PRFileDesc *fd,
+    const unsigned char* protos,
+    unsigned int protosLen,
+    unsigned char* protoOut,
+    unsigned int* protoOutLen,
+    unsigned int protoMaxOut);
+
+/* SSL_SetNextProtoCallback sets a callback function to handle Next Protocol
+ * Negotiation. It causes a client to advertise NPN. */
+SSL_IMPORT SECStatus SSL_SetNextProtoCallback(PRFileDesc *fd,
+                                              SSLNextProtoCallback callback,
+                                              void *arg);
+
+/* SSL_SetNextProtoNego can be used as an alternative to
+ * SSL_SetNextProtoCallback. It also causes a client to advertise NPN and
+ * installs a default callback function which selects the first supported
+ * protocol in server-preference order. If no matching protocol is found it
+ * selects the first supported protocol.
+ *
+ * The supported protocols are specified in |data| in wire-format (8-bit
+ * length-prefixed). For example: "\010http/1.1\006spdy/2". */
+SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd,
+					  const unsigned char *data,
+					  unsigned int length);
+
+typedef enum SSLNextProtoState { 
+  SSL_NEXT_PROTO_NO_SUPPORT = 0, /* No peer support                */
+  SSL_NEXT_PROTO_NEGOTIATED = 1, /* Mutual agreement               */
+  SSL_NEXT_PROTO_NO_OVERLAP = 2  /* No protocol overlap found      */
+} SSLNextProtoState;
+
+/* SSL_GetNextProto can be used in the HandshakeCallback or any time after
+ * a handshake to retrieve the result of the Next Protocol negotiation.
+ *
+ * The length of the negotiated protocol, if any, is written into *bufLen.
+ * If the negotiated protocol is longer than bufLenMax, then SECFailure is
+ * returned. Otherwise, the negotiated protocol, if any, is written into buf,
+ * and SECSuccess is returned. */
+SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd,
+				      SSLNextProtoState *state,
+				      unsigned char *buf,
+				      unsigned int *bufLen,
+				      unsigned int bufLenMax);
+
 /*
 ** Control ciphers that SSL uses. If on is non-zero then the named cipher
 ** is enabled, otherwise it is disabled. 
 ** The "cipher" values are defined in sslproto.h (the SSL_EN_* values).
 ** EnableCipher records user preferences.
 ** SetPolicy sets the policy according to the policy module.
 */
 #ifdef SSL_DEPRECATED_FUNCTION 
@@ -278,16 +334,29 @@ SSL_IMPORT SECStatus SSL_SecurityStatus(
 **	"fd" the socket "file" descriptor
 */
 SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
 
 /*
 ** Authenticate certificate hook. Called when a certificate comes in
 ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
 ** certificate.
+**
+** The authenticate certificate hook must return SECSuccess to indicate the
+** certificate is valid, SECFailure to indicate the certificate is invalid,
+** or SECWouldBlock if the application will authenticate the certificate
+** asynchronously.
+**
+** If the authenticate certificate hook returns SECFailure, then the bad cert
+** hook will be called. The bad cert handler is NEVER called if the
+** authenticate certificate hook returns SECWouldBlock.
+** 
+** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
+** information about the asynchronous behavior that occurs when the
+** authenticate certificate hook returns SECWouldBlock.
 */
 typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd, 
                                                     PRBool checkSig,
                                                     PRBool isServer);
 
 SSL_IMPORT SECStatus SSL_AuthCertificateHook(PRFileDesc *fd, 
 					     SSLAuthCertificate f,
 				             void *arg);
@@ -381,16 +450,25 @@ SSL_IMPORT PRFileDesc *SSL_ReconfigFD(PR
  *	a - pkcs11 application specific data
  */
 SSL_IMPORT SECStatus SSL_SetPKCS11PinArg(PRFileDesc *fd, void *a);
 
 /*
 ** This is a callback for dealing with server certs that are not authenticated
 ** by the client.  The client app can decide that it actually likes the
 ** cert by some external means and restart the connection.
+**
+** The bad cert hook must return SECSuccess to override the result of the
+** authenticate certificate hook, SECFailure if the certificate should still be
+** considered invalid, or SECWouldBlock if the application will authenticate
+** the certificate asynchronously.
+**
+** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
+** information about the asynchronous behavior that occurs when the bad cert
+** hook returns SECWouldBlock.
 */
 typedef SECStatus (PR_CALLBACK *SSLBadCertHandler)(void *arg, PRFileDesc *fd);
 SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, 
 				     void *arg);
 
 /*
 ** Configure SSL socket for running a secure server. Needs the
 ** certificate for the server and the servers private key. The arguments
@@ -679,11 +757,58 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
  */
 extern PRBool NSSSSL_VersionCheck(const char *importedVersion);
 
 /*
  * Returns a const string of the SSL library version.
  */
 extern const char *NSSSSL_GetVersion(void);
 
+/* Restart an SSL connection that was paused to do asynchronous certificate
+ * chain validation (when the auth certificate hook or bad cert handler
+ * returned SECWouldBlock).
+ *
+ * Currently, this function works only for the client role of a connection; it
+ * does not work for the server role.
+ *
+ * The application MUST call SSL_RestartHandshakeAfterAuthCertificate after it
+ * has successfully validated the peer's certificate to continue the SSL
+ * handshake.
+ *
+ * The application MUST NOT call SSL_RestartHandshakeAfterAuthCertificate when
+ * certificate validation fails; instead, it should just close the connection.
+ *
+ * This function will not complete the entire handshake. The application must
+ * call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function
+ * to force the handshake to complete.
+ *
+ * libssl will wait for the peer's certificate to be authenticated before
+ * calling the handshake callback, sending a client certificate,
+ * sending any application data, or returning any application data to the
+ * application (on the first handshake on a connection only).
+ *
+ * However, libssl may send and receive handshake messages while waiting for
+ * the application to call SSL_RestartHandshakeAfterAuthCertificate, and it may
+ * call other callbacks (e.g, the client auth data hook) before
+ * SSL_RestartHandshakeAfterAuthCertificate has been called. 
+ *
+ * An application that uses this asynchronous mechanism will usually have lower
+ * handshake latency if it has to do public key operations on the certificate
+ * chain during the authentication, especially if it does so in parallel on
+ * another thread. However, if the application can authenticate the peer's
+ * certificate quickly then it may be more efficient to use the synchronous
+ * mechanism (i.e. returning SECFailure/SECSuccess instead of SECWouldBlock
+ * from the authenticate certificate hook).
+ *
+ * Be careful about converting an application from synchronous cert validation
+ * to asynchronous certificate validation. A naive conversion is likely to
+ * result in deadlocks; e.g. the application will wait in PR_Poll for network
+ * I/O on the connection while all network I/O on the connection is blocked
+ * waiting for this function to be called.
+ *
+ * Returns SECFailure on failure, SECSuccess on success. Never returns
+ * SECWouldBlock.
+ */
+SSL_IMPORT SECStatus SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd);
+
 SEC_END_PROTOS
 
 #endif /* __ssl_h_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -34,17 +34,17 @@
  * 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 ***** */
-/* $Id: ssl3con.c,v 1.152 2011/10/01 03:59:54 bsmith%mozilla.com Exp $ */
+/* $Id: ssl3con.c,v 1.158 2011/11/19 21:58:21 bsmith%mozilla.com Exp $ */
 
 #include "cert.h"
 #include "ssl.h"
 #include "cryptohi.h"	/* for DSAU_ stuff */
 #include "keyhi.h"
 #include "secder.h"
 #include "secitem.h"
 
@@ -76,16 +76,17 @@ static PK11SymKey *ssl3_GenerateRSAPMS(s
                                        PK11SlotInfo * serverKeySlot);
 static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
 static SECStatus ssl3_DeriveConnectionKeysPKCS11(sslSocket *ss);
 static SECStatus ssl3_HandshakeFailure(      sslSocket *ss);
 static SECStatus ssl3_InitState(             sslSocket *ss);
 static SECStatus ssl3_SendCertificate(       sslSocket *ss);
 static SECStatus ssl3_SendEmptyCertificate(  sslSocket *ss);
 static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
+static SECStatus ssl3_SendNextProto(         sslSocket *ss);
 static SECStatus ssl3_SendFinished(          sslSocket *ss, PRInt32 flags);
 static SECStatus ssl3_SendServerHello(       sslSocket *ss);
 static SECStatus ssl3_SendServerHelloDone(   sslSocket *ss);
 static SECStatus ssl3_SendServerKeyExchange( sslSocket *ss);
 static SECStatus ssl3_NewHandshakeHashes(    sslSocket *ss);
 static SECStatus ssl3_UpdateHandshakeHashes( sslSocket *ss,
                                              const unsigned char *b,
                                              unsigned int l);
@@ -232,19 +233,16 @@ static const /*SSL3ClientCertificateType
  * make sure there is room in the write buffer for padding and
  * other compression and cryptographic expansions.
  */
 #define SSL3_BUFFER_FUDGE     100 + SSL3_COMPRESSION_MAX_EXPANSION
 
 #define EXPORT_RSA_KEY_LENGTH 64	/* bytes */
 
 
-/* This is a hack to make sure we don't do double handshakes for US policy */
-PRBool ssl3_global_policy_some_restricted = PR_FALSE;
-
 /* This global item is used only in servers.  It is is initialized by
 ** SSL_ConfigSecureServer(), and is used in ssl3_SendCertificateRequest().
 */
 CERTDistNames *ssl3_server_ca_list = NULL;
 static SSL3Statistics ssl3stats;
 
 /* indexed by SSL3BulkCipher */
 static const ssl3BulkCipherDef bulk_cipher_defs[] = {
@@ -3754,17 +3752,16 @@ done:
 }
 
 /**************************************************************************
  * end of Handshake Hash functions.
  * Begin Send and Handle functions for handshakes.
  **************************************************************************/
 
 /* Called from ssl3_HandleHelloRequest(),
- *             ssl3_HandleFinished() (for step-up)
  *             ssl3_RedoHandshake()
  *             ssl2_BeginClientHandshake (when resuming ssl3 session)
  */
 SECStatus
 ssl3_SendClientHello(sslSocket *ss)
 {
     sslSessionID *   sid;
     ssl3CipherSpec * cwSpec;
@@ -5578,17 +5575,17 @@ ssl3_HandleCertificateRequest(sslSocket 
 	/* XXX Should pass cert_types in this call!! */
 	rv = (SECStatus)(*ss->getClientAuthData)(ss->getClientAuthDataArg,
 						 ss->fd, &ca_list,
 						 &ss->ssl3.clientCertificate,
 						 &ss->ssl3.clientPrivateKey);
     }
     switch (rv) {
     case SECWouldBlock:	/* getClientAuthData has put up a dialog box. */
-	ssl_SetAlwaysBlock(ss);
+	ssl3_SetAlwaysBlock(ss);
 	break;	/* not an error */
 
     case SECSuccess:
         /* check what the callback function returned */
         if ((!ss->ssl3.clientCertificate) || (!ss->ssl3.clientPrivateKey)) {
             /* we are missing either the key or cert */
             if (ss->ssl3.clientCertificate) {
                 /* got a cert, but no key - free it */
@@ -5647,152 +5644,168 @@ loser:
     PORT_SetError(errCode);
     rv = SECFailure;
 done:
     if (arena != NULL)
     	PORT_FreeArena(arena, PR_FALSE);
     return rv;
 }
 
-/*
- * attempt to restart the handshake after asynchronously handling
- * a request for the client's certificate.
- *
- * inputs:
- *	cert	Client cert chosen by application.
- *		Note: ssl takes this reference, and does not bump the
- *		reference count.  The caller should drop its reference
- *		without calling CERT_DestroyCert after calling this function.
- *
- *	key	Private key associated with cert.  This function makes a
- *		copy of the private key, so the caller remains responsible
- *		for destroying its copy after this function returns.
- *
- *	certChain  DER-encoded certs, client cert and its signers.
- *		Note: ssl takes this reference, and does not copy the chain.
- *		The caller should drop its reference without destroying the
- *		chain.  SSL will free the chain when it is done with it.
- *
- * Return value: XXX
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Caller holds 1stHandshakeLock.
- */
-SECStatus
-ssl3_RestartHandshakeAfterCertReq(sslSocket *         ss,
-				CERTCertificate *    cert,
-				SECKEYPrivateKey *   key,
-				CERTCertificateList *certChain)
-{
-    SECStatus        rv          = SECSuccess;
-
-    if (MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0)) {
-	/* XXX This code only works on the initial handshake on a connection,
-	** XXX It does not work on a subsequent handshake (redo).
-	*/
-	if (ss->handshake != 0) {
-	    ss->handshake               = ssl_GatherRecord1stHandshake;
-	    ss->ssl3.clientCertificate = cert;
-	    ss->ssl3.clientCertChain   = certChain;
-	    if (key == NULL) {
-		(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
-		ss->ssl3.clientPrivateKey = NULL;
-	    } else {
-		ss->ssl3.clientPrivateKey = SECKEY_CopyPrivateKey(key);
-	    }
-	    ssl_GetRecvBufLock(ss);
-	    if (ss->ssl3.hs.msgState.buf != NULL) {
-		rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
-	    }
-	    ssl_ReleaseRecvBufLock(ss);
-	}
-    }
-    return rv;
-}
-
 PRBool
 ssl3_CanFalseStart(sslSocket *ss) {
     PRBool rv;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
+    /* XXX: does not take into account whether we are waiting for
+     * SSL_RestartHandshakeAfterAuthCertificate or
+     * SSL_RestartHandshakeAfterCertReq. If/when that is done, this function
+     * could return different results each time it would be called.
+     */
+
     ssl_GetSpecReadLock(ss);
     rv = ss->opt.enableFalseStart &&
 	 !ss->sec.isServer &&
 	 !ss->ssl3.hs.isResuming &&
 	 ss->ssl3.cwSpec &&
 	 ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
 	(ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
     ssl_ReleaseSpecReadLock(ss);
     return rv;
 }
 
+static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
+
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Server Hello Done message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleServerHelloDone(sslSocket *ss)
 {
     SECStatus     rv;
     SSL3WaitState ws          = ss->ssl3.hs.ws;
-    PRBool        send_verify = PR_FALSE;
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
 		SSL_GETPID(), ss->fd));
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
     if (ws != wait_hello_done  &&
         ws != wait_server_cert &&
 	ws != wait_server_key  &&
 	ws != wait_cert_request) {
 	SSL3_SendAlert(ss, alert_fatal, unexpected_message);
 	PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
 	return SECFailure;
     }
 
+    rv = ssl3_SendClientSecondRound(ss);
+
+    return rv;
+}
+
+/* Called from ssl3_HandleServerHelloDone and
+ * ssl3_RestartHandshakeAfterServerCert.
+ *
+ * Caller must hold Handshake and RecvBuf locks.
+ */
+static SECStatus
+ssl3_SendClientSecondRound(sslSocket *ss)
+{
+    SECStatus rv;
+    PRBool sendClientCert;
+
+    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+
+    sendClientCert = !ss->ssl3.sendEmptyCert &&
+		     ss->ssl3.clientCertChain  != NULL &&
+		     ss->ssl3.clientPrivateKey != NULL;
+
+    /* We must wait for the server's certificate to be authenticated before
+     * sending the client certificate in order to disclosing the client
+     * certificate to an attacker that does not have a valid cert for the
+     * domain we are connecting to.
+     *
+     * XXX: We should do the same for the NPN extension, but for that we
+     * need an option to give the application the ability to leak the NPN
+     * information to get better performance.
+     *
+     * During the initial handshake on a connection, we never send/receive
+     * application data until we have authenticated the server's certificate;
+     * i.e. we have fully authenticated the handshake before using the cipher
+     * specs agreed upon for that handshake. During a renegotiation, we may
+     * continue sending and receiving application data during the handshake
+     * interleaved with the handshake records. If we were to send the client's
+     * second round for a renegotiation before the server's certificate was
+     * authenticated, then the application data sent/received after this point
+     * would be using cipher spec that hadn't been authenticated. By waiting
+     * until the server's certificate has been authenticated during 
+     * renegotiations, we ensure that renegotiations have the same property
+     * as initial handshakes; i.e. we have fully authenticated the handshake
+     * before using the cipher specs agreed upon for that handshake for
+     * application data.
+     */
+    if (ss->ssl3.hs.restartTarget) {
+        PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+        return SECFailure;
+    }
+    if (ss->ssl3.hs.authCertificatePending &&
+        (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
+        ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
+        return SECWouldBlock;
+    }
+
     ssl_GetXmitBufLock(ss);		/*******************************/
 
     if (ss->ssl3.sendEmptyCert) {
 	ss->ssl3.sendEmptyCert = PR_FALSE;
 	rv = ssl3_SendEmptyCertificate(ss);
 	/* Don't send verify */
 	if (rv != SECSuccess) {
 	    goto loser;	/* error code is set. */
     	}
-    } else
-    if (ss->ssl3.clientCertChain  != NULL &&
-	ss->ssl3.clientPrivateKey != NULL) {
-	send_verify = PR_TRUE;
+    } else if (sendClientCert) {
 	rv = ssl3_SendCertificate(ss);
 	if (rv != SECSuccess) {
 	    goto loser;	/* error code is set. */
     	}
     }
 
     rv = ssl3_SendClientKeyExchange(ss);
     if (rv != SECSuccess) {
     	goto loser;	/* err is set. */
     }
 
-    if (send_verify) {
+    if (sendClientCert) {
 	rv = ssl3_SendCertificateVerify(ss);
 	if (rv != SECSuccess) {
 	    goto loser;	/* err is set. */
         }
     }
+
     rv = ssl3_SendChangeCipherSpecs(ss);
     if (rv != SECSuccess) {
 	goto loser;	/* err code was set. */
     }
+
+    /* XXX: If the server's certificate hasn't been authenticated by this
+     * point, then we may be leaking this NPN message to an attacker.
+     */
+    if (!ss->firstHsDone) {
+	rv = ssl3_SendNextProto(ss);
+	if (rv != SECSuccess) {
+	    goto loser;	/* err code was set. */
+	}
+    }
+
     rv = ssl3_SendFinished(ss, 0);
     if (rv != SECSuccess) {
 	goto loser;	/* err code was set. */
     }
 
     ssl_ReleaseXmitBufLock(ss);		/*******************************/
 
     if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
@@ -7804,19 +7817,16 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
  * ssl3 Certificate message.
  * Caller must hold Handshake and RecvBuf locks.
  */
 static SECStatus
 ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
 {
     ssl3CertNode *   c;
     ssl3CertNode *   lastCert 	= NULL;
-    ssl3CertNode *   certs 	= NULL;
-    PRArenaPool *    arena 	= NULL;
-    CERTCertificate *cert;
     PRInt32          remaining  = 0;
     PRInt32          size;
     SECStatus        rv;
     PRBool           isServer	= (PRBool)(!!ss->sec.isServer);
     PRBool           trusted 	= PR_FALSE;
     PRBool           isTLS;
     SSL3AlertDescription desc	= bad_certificate;
     int              errCode    = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
@@ -7863,21 +7873,21 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 	    goto alert_loser;
     	/* This is TLS's version of a no_certificate alert. */
     	/* I'm a server. I've requested a client cert. He hasn't got one. */
 	rv = ssl3_HandleNoCertificate(ss);
 	if (rv != SECSuccess) {
 	    errCode = PORT_GetError();
 	    goto loser;
 	}
-	goto cert_block;
-    }
-
-    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if ( arena == NULL ) {
+	goto server_no_cert;
+    }
+
+    ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (ss->ssl3.peerCertArena == NULL) {
 	goto loser;	/* don't send alerts on memory errors */
     }
 
     /* First get the peer cert. */
     remaining -= 3;
     if (remaining < 0)
 	goto decode_loser;
 
@@ -7917,17 +7927,17 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 	    goto decode_loser;
 
 	certItem.data = b;
 	certItem.len = size;
 	b      += size;
 	length -= size;
 	remaining -= size;
 
-	c = PORT_ArenaNew(arena, ssl3CertNode);
+	c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode);
 	if (c == NULL) {
 	    goto loser;	/* don't send alerts on memory errors */
 	}
 
 	c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
 	                                  PR_FALSE, PR_TRUE);
 	if (c->cert == NULL) {
 	    goto ambiguous_err;
@@ -7935,70 +7945,62 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 
 	if (c->cert->trust)
 	    trusted = PR_TRUE;
 
 	c->next = NULL;
 	if (lastCert) {
 	    lastCert->next = c;
 	} else {
-	    certs = c;
+	    ss->ssl3.peerCertChain = c;
 	}
 	lastCert = c;
     }
 
     if (remaining != 0)
         goto decode_loser;
 
     SECKEY_UpdateCertPQG(ss->sec.peerCert);
 
+    ss->ssl3.hs.authCertificatePending = PR_FALSE;
+
     /*
      * Ask caller-supplied callback function to validate cert chain.
      */
     rv = (SECStatus)(*ss->authCertificate)(ss->authCertificateArg, ss->fd,
 					   PR_TRUE, isServer);
     if (rv) {
 	errCode = PORT_GetError();
-	if (!ss->handleBadCert) {
-	    goto bad_cert;
-	}
-	rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd);
-	if ( rv ) {
-	    if ( rv == SECWouldBlock ) {
-		/* someone will handle this connection asynchronously*/
-		SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
-			 SSL_GETPID(), ss->fd));
-		ss->ssl3.peerCertChain = certs;
-		certs               = NULL;
-		ssl_SetAlwaysBlock(ss);
-		goto cert_block;
+	if (rv != SECWouldBlock) {
+	    if (ss->handleBadCert) {
+		rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
 	    }
-	    /* cert is bad */
+	}
+
+	if (rv == SECWouldBlock) {
+	    if (ss->sec.isServer) {
+		errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
+		rv = SECFailure;
+		goto loser;
+	    }
+
+            ss->ssl3.hs.authCertificatePending = PR_TRUE;
+            rv = SECSuccess;
+	}
+        
+        if (rv != SECSuccess) {
 	    goto bad_cert;
 	}
-	/* cert is good */
-    }
-
-    /* start SSL Step Up, if appropriate */
-    cert = ss->sec.peerCert;
-    if (!isServer &&
-    	ssl3_global_policy_some_restricted &&
-        ss->ssl3.policy == SSL_ALLOWED &&
-	anyRestrictedEnabled(ss) &&
-	SECSuccess == CERT_VerifyCertNow(cert->dbhandle, cert,
-	                                 PR_FALSE, /* checkSig */
-				         certUsageSSLServerWithStepUp,
-/*XXX*/				         ss->authCertificateArg) ) {
-	ss->ssl3.policy         = SSL_RESTRICTED;
-	ss->ssl3.hs.rehandshake = PR_TRUE;
     }
 
     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
 
     if (!ss->sec.isServer) {
+        CERTCertificate *cert = ss->sec.peerCert;
+
 	/* set the server authentication and key exchange types and sizes
 	** from the value in the cert.  If the key exchange key is different,
 	** it will get fixed when we handle the server key exchange message.
 	*/
 	SECKEYPublicKey * pubKey  = CERT_ExtractPublicKey(cert);
 	ss->sec.authAlgorithm = ss->ssl3.hs.kea_def->signKeyType;
 	ss->sec.keaType       = ss->ssl3.hs.kea_def->exchKeyType;
 	if (pubKey) {
@@ -8029,39 +8031,38 @@ ssl3_HandleCertificate(sslSocket *ss, SS
 		     * destroy pubKey and goto bad_cert
 		     */
 		}
 	    }
 #endif /* NSS_ENABLE_ECC */
 	    SECKEY_DestroyPublicKey(pubKey); 
 	    pubKey = NULL;
     	}
-    }
-
-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
-
-cert_block:
-    if (ss->sec.isServer) {
-	ss->ssl3.hs.ws = wait_client_key;
-    } else {
+
 	ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */
 	if (ss->ssl3.hs.kea_def->is_limited ||
 	    /* XXX OR server cert is signing only. */
 #ifdef NSS_ENABLE_ECC
 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
 #endif /* NSS_ENABLE_ECC */
 	    ss->ssl3.hs.kea_def->exchKeyType == kt_dh) {
 	    ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */
 	}
-    }
-
-    /* rv must normally be equal to SECSuccess here.  If we called
-     * handleBadCert, it can also be SECWouldBlock.
-     */
+    } else {
+server_no_cert:
+	ss->ssl3.hs.ws = wait_client_key;
+    }
+
+    PORT_Assert(rv == SECSuccess);
+    if (rv != SECSuccess) {
+	errCode = SEC_ERROR_LIBRARY_FAILURE;
+	rv = SECFailure;
+	goto loser;
+    }
     return rv;
 
 ambiguous_err:
     errCode = PORT_GetError();
     switch (errCode) {
     case PR_OUT_OF_MEMORY_ERROR:
     case SEC_ERROR_BAD_DATABASE:
     case SEC_ERROR_NO_MEMORY:
@@ -8102,79 +8103,68 @@ bad_cert:	/* caller has set errCode. */
 
 decode_loser:
     desc = isTLS ? decode_error : bad_certificate;
 
 alert_loser:
     (void)SSL3_SendAlert(ss, alert_fatal, desc);
 
 loser:
-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
     ssl3_CleanupPeerCerts(ss);
 
     if (ss->sec.peerCert != NULL) {
 	CERT_DestroyCertificate(ss->sec.peerCert);
 	ss->sec.peerCert = NULL;
     }
     (void)ssl_MapLowLevelError(errCode);
     return SECFailure;
 }
 
-
-/* restart an SSL connection that we stopped to run certificate dialogs
-** XXX	Need to document here how an application marks a cert to show that
-**	the application has accepted it (overridden CERT_VerifyCert).
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Return value: XXX
- *
- * Caller holds 1stHandshakeLock.
+static SECStatus ssl3_FinishHandshake(sslSocket *ss);
+
+/* Caller must hold 1stHandshakeLock.
 */
-int
-ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
-{
-    CERTCertificate * cert;
-    int               rv	= SECSuccess;
-
-    if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
-	SET_ERROR_CODE
-    	return SECFailure;
-    }
-    if (!ss->ssl3.initialized) {
-	SET_ERROR_CODE
-    	return SECFailure;
-    }
-
-    cert = ss->sec.peerCert;
-
-    /* Permit step up if user decided to accept the cert */
-    if (!ss->sec.isServer &&
-    	ssl3_global_policy_some_restricted &&
-        ss->ssl3.policy == SSL_ALLOWED &&
-	anyRestrictedEnabled(ss) &&
-	(SECSuccess == CERT_VerifyCertNow(cert->dbhandle, cert,
-	                                  PR_FALSE, /* checksig */
-				          certUsageSSLServerWithStepUp,
-/*XXX*/				          ss->authCertificateArg) )) {
-	ss->ssl3.policy         = SSL_RESTRICTED;
-	ss->ssl3.hs.rehandshake = PR_TRUE;
-    }
-
-    if (ss->handshake != NULL) {
-	ss->handshake = ssl_GatherRecord1stHandshake;
-	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
-
-	ssl_GetRecvBufLock(ss);
-	if (ss->ssl3.hs.msgState.buf != NULL) {
-	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
-	}
-	ssl_ReleaseRecvBufLock(ss);
-    }
+SECStatus
+ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss)
+{
+    SECStatus rv;
+
+    PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
+
+    if (ss->sec.isServer) {
+	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS);
+	return SECFailure;
+    }
+
+    ssl_GetRecvBufLock(ss);
+    ssl_GetSSL3HandshakeLock(ss);
+
+    if (!ss->ssl3.hs.authCertificatePending) {
+        PORT_SetError(PR_INVALID_STATE_ERROR);
+        rv = SECFailure;
+    } else {
+        ss->ssl3.hs.authCertificatePending = PR_FALSE;
+        if (ss->ssl3.hs.restartTarget != NULL) {
+            sslRestartTarget target = ss->ssl3.hs.restartTarget;
+            ss->ssl3.hs.restartTarget = NULL;
+            rv = target(ss);
+	    /* Even if we blocked here, we have accomplished enough to claim
+	      * success. Any remaining work will be taken care of by subsequent
+              * calls to SSL_ForceHandshake/PR_Send/PR_Read/etc. 
+	      */
+            if (rv == SECWouldBlock) {
+                rv = SECSuccess;
+            }
+        } else {
+            rv = SECSuccess;
+        }
+    }
+
+    ssl_ReleaseSSL3HandshakeLock(ss);
+    ssl_ReleaseRecvBufLock(ss);
 
     return rv;
 }
 
 static SECStatus
 ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
 			PRBool          isServer,
                 const   SSL3Finished *  hashes,
@@ -8216,16 +8206,50 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *
 	rv = TLS_PRF(&spec->msItem, label, &inData, &outData, isFIPS);
 	PORT_Assert(rv != SECSuccess || \
 		    outData.len == sizeof tlsFinished->verify_data);
     }
     return rv;
 }
 
 /* called from ssl3_HandleServerHelloDone
+ */
+static SECStatus
+ssl3_SendNextProto(sslSocket *ss)
+{
+    SECStatus rv;
+    int padding_len;
+    static const unsigned char padding[32] = {0};
+
+    if (ss->ssl3.nextProto.len == 0)
+	return SECSuccess;
+
+    PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
+    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+
+    padding_len = 32 - ((ss->ssl3.nextProto.len + 2) % 32);
+
+    rv = ssl3_AppendHandshakeHeader(ss, next_proto, ss->ssl3.nextProto.len +
+						    2 + padding_len);
+    if (rv != SECSuccess) {
+	return rv;	/* error code set by AppendHandshakeHeader */
+    }
+    rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data,
+				      ss->ssl3.nextProto.len, 1);
+    if (rv != SECSuccess) {
+	return rv;	/* error code set by AppendHandshake */
+    }
+    rv = ssl3_AppendHandshakeVariable(ss, padding, padding_len, 1);
+    if (rv != SECSuccess) {
+	return rv;	/* error code set by AppendHandshake */
+    }
+    return rv;
+}
+
+/* called from ssl3_HandleServerHelloDone
  *             ssl3_HandleClientHello
  *             ssl3_HandleFinished
  */
 static SECStatus
 ssl3_SendFinished(sslSocket *ss, PRInt32 flags)
 {
     ssl3CipherSpec *cwSpec;
     PRBool          isTLS;
@@ -8377,17 +8401,16 @@ ssl3_CacheWrappedMasterSecret(sslSocket 
 static SECStatus
 ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
 		    const SSL3Hashes *hashes)
 {
     sslSessionID *    sid	   = ss->sec.ci.sid;
     SECStatus         rv           = SECSuccess;
     PRBool            isServer     = ss->sec.isServer;
     PRBool            isTLS;
-    PRBool            doStepUp;
     SSL3KEAType       effectiveExchKeyType;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
 
     SSL_TRC(3, ("%d: SSL3[%d]: handle finished handshake",
     	SSL_GETPID(), ss->fd));
 
@@ -8433,18 +8456,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
 	ss->ssl3.hs.finishedBytes = sizeof *hashes;
 	if (0 != NSS_SecureMemcmp(hashes, b, length)) {
 	    (void)ssl3_HandshakeFailure(ss);
 	    PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
 	    return SECFailure;
 	}
     }
 
-    doStepUp = (PRBool)(!isServer && ss->ssl3.hs.rehandshake);
-
     ssl_GetXmitBufLock(ss);	/*************************************/
 
     if ((isServer && !ss->ssl3.hs.isResuming) ||
 	(!isServer && ss->ssl3.hs.isResuming)) {
 	PRInt32 flags = 0;
 
 	/* Send a NewSessionTicket message if the client sent us
 	 * either an empty session ticket, or one that did not verify.
@@ -8460,46 +8481,43 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
 	    }
 	}
 
 	rv = ssl3_SendChangeCipherSpecs(ss);
 	if (rv != SECSuccess) {
 	    goto xmit_loser;	/* err is set. */
 	}
 	/* If this thread is in SSL_SecureSend (trying to write some data) 
-	** or if it is going to step up, 
 	** then set the ssl_SEND_FLAG_FORCE_INTO_BUFFER flag, so that the 
 	** last two handshake messages (change cipher spec and finished) 
 	** will be sent in the same send/write call as the application data.
 	*/
-	if (doStepUp || ss->writerThread == PR_GetCurrentThread()) {
+	if (ss->writerThread == PR_GetCurrentThread()) {
 	    flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER;
 	}
+
+	if (!isServer && !ss->firstHsDone) {
+	    rv = ssl3_SendNextProto(ss);
+	    if (rv != SECSuccess) {
+		goto xmit_loser; /* err code was set. */
+	    }
+	}
+
 	rv = ssl3_SendFinished(ss, flags);
 	if (rv != SECSuccess) {
 	    goto xmit_loser;	/* err is set. */
 	}
     }
 
-    /* Optimization: don't cache this connection if we're going to step up. */
-    if (doStepUp) {
-	ssl_FreeSID(sid);
-	ss->sec.ci.sid     = sid = NULL;
-	ss->ssl3.hs.rehandshake = PR_FALSE;
-	rv = ssl3_SendClientHello(ss);
 xmit_loser:
-	ssl_ReleaseXmitBufLock(ss);
-	return rv;	/* err code is set if appropriate. */
-    }
-
     ssl_ReleaseXmitBufLock(ss);	/*************************************/
-
-    /* The first handshake is now completed. */
-    ss->handshake           = NULL;
-    ss->firstHsDone         = PR_TRUE;
+    if (rv != SECSuccess) {
+        return rv;
+    }
+
     ss->gs.writeOffset = 0;
     ss->gs.readOffset  = 0;
 
     if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
 	effectiveExchKeyType = kt_rsa;
     } else {
 	effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
     }
@@ -8539,20 +8557,52 @@ xmit_loser:
 					       effectiveExchKeyType);
 	    sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
 	}
 	ssl_ReleaseSpecReadLock(ss);  /*************************************/
 
 	/* If the wrap failed, we don't cache the sid.
 	 * The connection continues normally however.
 	 */
-	if (rv == SECSuccess) {
-	    (*ss->sec.cache)(sid);
-	}
-    }
+	ss->ssl3.hs.cacheSID = rv == SECSuccess;
+    }
+
+    if (ss->ssl3.hs.authCertificatePending) {
+      if (ss->ssl3.hs.restartTarget) {
+          PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
+          PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+          return SECFailure;
+      }
+
+      ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
+      return SECWouldBlock;
+    }
+    
+    rv = ssl3_FinishHandshake(ss);
+    return rv;
+}
+
+SECStatus
+ssl3_FinishHandshake(sslSocket * ss)
+{
+    SECStatus rv;
+    
+    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+    PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
+
+    /* The first handshake is now completed. */
+    ss->handshake           = NULL;
+    ss->firstHsDone         = PR_TRUE;
+
+    if (ss->sec.ci.sid->cached == never_cached &&
+	!ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) {
+	(*ss->sec.cache)(ss->sec.ci.sid);
+    }
+
     ss->ssl3.hs.ws = idle_handshake;
 
     /* Do the handshake callback for sslv3 here, if we cannot false start. */
     if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
 	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
     }
 
     return SECSuccess;
@@ -9201,17 +9251,16 @@ ssl3_InitState(sslSocket *ss)
     if (ss->ssl3.initialized)
     	return SECSuccess;	/* Function should be idempotent */
 
     ss->ssl3.policy = SSL_ALLOWED;
 
     ssl_GetSpecWriteLock(ss);
     ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0];
     ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1];
-    ss->ssl3.hs.rehandshake = PR_FALSE;
     ss->ssl3.hs.sendingSCSV = PR_FALSE;
     ssl3_InitCipherSpec(ss, ss->ssl3.crSpec);
     ssl3_InitCipherSpec(ss, ss->ssl3.prSpec);
 
     ss->ssl3.hs.ws = (ss->sec.isServer) ? wait_client_hello : wait_server_hello;
 #ifdef NSS_ENABLE_ECC
     ss->ssl3.hs.negotiatedECCurves = SSL3_SUPPORTED_CURVES_MASK;
 #endif
@@ -9310,20 +9359,16 @@ ssl3_SetPolicy(ssl3CipherSuite which, in
     ssl3CipherSuiteCfg *suite;
 
     suite = ssl_LookupCipherSuiteCfg(which, cipherSuites);
     if (suite == NULL) {
 	return SECFailure; /* err code was set by ssl_LookupCipherSuiteCfg */
     }
     suite->policy = policy;
 
-    if (policy == SSL_RESTRICTED) {
-	ssl3_global_policy_some_restricted = PR_TRUE;
-    }
-
     return SECSuccess;
 }
 
 SECStatus
 ssl3_GetPolicy(ssl3CipherSuite which, PRInt32 *oPolicy)
 {
     ssl3CipherSuiteCfg *suite;
     PRInt32             policy;
@@ -9535,11 +9580,13 @@ ssl3_DestroySSL3Info(sslSocket *ss)
     /* free the SSL3Buffer (msg_body) */
     PORT_Free(ss->ssl3.hs.msg_body.buf);
 
     /* free up the CipherSpecs */
     ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
     ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
 
     ss->ssl3.initialized = PR_FALSE;
+
+    SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
 }
 
 /* End of ssl3con.c */
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -36,17 +36,17 @@
  * 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 ***** */
 
 /* TLS extension code moved here from ssl3ecc.c */
-/* $Id: ssl3ext.c,v 1.16 2011/03/24 01:40:14 alexei.volkov.bugs%sun.com Exp $ */
+/* $Id: ssl3ext.c,v 1.20 2011/11/16 19:12:35 kaie%kuix.de Exp $ */
 
 #include "nssrenam.h"
 #include "nss.h"
 #include "ssl.h"
 #include "sslproto.h"
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "blapi.h"
@@ -73,16 +73,22 @@ static SECStatus ssl3_GetSessionTicketKe
     PK11SymKey **aes_key, PK11SymKey **mac_key);
 static SECStatus ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
     PRUint32 *aes_key_length, const unsigned char **mac_key,
     PRUint32 *mac_key_length);
 static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket * ss,
     PRBool append, PRUint32 maxBytes);
 static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, 
     PRUint16 ex_type, SECItem *data);
+static SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss,
+			PRUint16 ex_type, SECItem *data);
+static SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
+			PRUint16 ex_type, SECItem *data);
+static PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
+					       PRUint32 maxBytes);
 
 /*
  * Write bytes.  Using this function means the SECItem structure
  * cannot be freed.  The caller is expected to call this function
  * on a shallow copy of the structure.
  */
 static SECStatus
 ssl3_AppendToItem(SECItem *item, const unsigned char *buf, PRUint32 bytes)
@@ -230,26 +236,28 @@ ssl3_GetSessionTicketKeys(const unsigned
 static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
     { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
 #ifdef NSS_ENABLE_ECC
     { ssl_elliptic_curves_xtn,    &ssl3_HandleSupportedCurvesXtn },
     { ssl_ec_point_formats_xtn,   &ssl3_HandleSupportedPointFormatsXtn },
 #endif
     { ssl_session_ticket_xtn,     &ssl3_ServerHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ServerHandleNextProtoNegoXtn },
     { -1, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
     /* TODO: add a handler for ssl_ec_point_formats_xtn */
     { ssl_session_ticket_xtn,     &ssl3_ClientHandleSessionTicketXtn },
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ClientHandleNextProtoNegoXtn },
     { -1, NULL }
 };
 
 static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { -1, NULL }
 };
 
@@ -262,17 +270,18 @@ static const ssl3HelloExtensionHandler s
 static const 
 ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
     { ssl_server_name_xtn,        &ssl3_SendServerNameXtn        },
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
 #ifdef NSS_ENABLE_ECC
     { ssl_elliptic_curves_xtn,    &ssl3_SendSupportedCurvesXtn },
     { ssl_ec_point_formats_xtn,   &ssl3_SendSupportedPointFormatsXtn },
 #endif
-    { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn }
+    { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ClientSendNextProtoNegoXtn }
     /* any extra entries will appear as { 0, NULL }    */
 };
 
 static const 
 ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
     { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
     /* any extra entries will appear as { 0, NULL }    */
 };
@@ -529,16 +538,132 @@ ssl3_SendSessionTicketXtn(
     }
     return extension_length;
 
  loser:
     ss->xtnData.ticketTimestampVerified = PR_FALSE;
     return -1;
 }
 
+/* handle an incoming Next Protocol Negotiation extension. */
+static SECStatus
+ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
+{
+    if (ss->firstHsDone || data->len != 0) {
+	/* Clients MUST send an empty NPN extension, if any. */
+	PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+	return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none
+ * of the lengths may be 0 and the sum of the lengths must equal the length of
+ * the block. */
+SECStatus
+ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned int length)
+{
+    unsigned int offset = 0;
+
+    while (offset < length) {
+	unsigned int newOffset = offset + 1 + (unsigned int) data[offset];
+	/* Reject embedded nulls to protect against buggy applications that
+	 * store protocol identifiers in null-terminated strings.
+	 */
+	if (newOffset > length || data[offset] == 0) {
+	    PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+	    return SECFailure;
+	}
+	offset = newOffset;
+    }
+
+    if (offset > length) {
+	PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+	return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+static SECStatus
+ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
+				  SECItem *data)
+{
+    SECStatus rv;
+    unsigned char resultBuffer[255];
+    SECItem result = { siBuffer, resultBuffer, 0 };
+
+    if (ss->firstHsDone) {
+	PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
+	return SECFailure;
+    }
+
+    rv = ssl3_ValidateNextProtoNego(data->data, data->len);
+    if (rv != SECSuccess)
+	return rv;
+
+    /* ss->nextProtoCallback cannot normally be NULL if we negotiated the
+     * extension. However, It is possible that an application erroneously
+     * cleared the callback between the time we sent the ClientHello and now.
+     */
+    PORT_Assert(ss->nextProtoCallback != NULL);
+    if (!ss->nextProtoCallback) {
+	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+	return SECFailure;
+    }
+
+    rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
+			       result.data, &result.len, sizeof resultBuffer);
+    if (rv != SECSuccess)
+	return rv;
+    /* If the callback wrote more than allowed to |result| it has corrupted our
+     * stack. */
+    if (result.len > sizeof result) {
+	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+	return SECFailure;
+    }
+
+    SECITEM_FreeItem(&ss->ssl3.nextProto, PR_FALSE);
+    return SECITEM_CopyItem(NULL, &ss->ssl3.nextProto, &result);
+}
+
+static PRInt32
+ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss, PRBool append,
+				PRUint32 maxBytes)
+{
+    PRInt32 extension_length;
+
+    /* Renegotiations do not send this extension. */
+    if (!ss->nextProtoCallback || ss->firstHsDone) {
+	return 0;
+    }
+
+    extension_length = 4;
+
+    if (append && maxBytes >= extension_length) {
+	SECStatus rv;
+	rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2);
+	if (rv != SECSuccess)
+	    goto loser;
+	rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+	if (rv != SECSuccess)
+	    goto loser;
+	ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+		ssl_next_proto_neg_xtn;
+    } else if (maxBytes < extension_length) {
+	return 0;
+    }
+
+    return extension_length;
+
+loser:
+    return -1;
+}
+
 /*
  * NewSessionTicket
  * Called from ssl3_HandleFinished
  */
 SECStatus
 ssl3_SendNewSessionTicket(sslSocket *ss)
 {
     int                  i;
--- a/security/nss/lib/ssl/ssl3gthr.c
+++ b/security/nss/lib/ssl/ssl3gthr.c
@@ -187,31 +187,63 @@ int
 ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
 {
     SSL3Ciphertext cText;
     int            rv;
     PRBool         canFalseStart = PR_FALSE;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     do {
-	/* bring in the next sslv3 record. */
-	rv = ssl3_GatherData(ss, &ss->gs, flags);
-	if (rv <= 0) {
-	    return rv;
-	}
-	
-	/* decipher it, and handle it if it's a handshake. 
-	 * If it's application data, ss->gs.buf will not be empty upon return. 
-	 * If it's a change cipher spec, alert, or handshake message,
-	 * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
-	 */
-	cText.type    = (SSL3ContentType)ss->gs.hdr[0];
-	cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
-	cText.buf     = &ss->gs.inbuf;
-	rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+        /* Without this, we may end up wrongly reporting
+         * SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
+         * peer while we are waiting to be restarted. 
+         */
+        ssl_GetSSL3HandshakeLock(ss);
+        rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure;
+        ssl_ReleaseSSL3HandshakeLock(ss);
+        if (rv != SECSuccess) {
+            PORT_SetError(PR_WOULD_BLOCK_ERROR);
+            return (int) SECFailure;
+        }
+
+        /* Treat an empty msgState like a NULL msgState. (Most of the time
+         * when ssl3_HandleHandshake returns SECWouldBlock, it leaves
+         * behind a non-NULL but zero-length msgState).
+         * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
+         */
+        if (ss->ssl3.hs.msgState.buf != NULL) {
+            if (ss->ssl3.hs.msgState.len == 0) {
+                ss->ssl3.hs.msgState.buf = NULL;
+            }
+        }
+
+        if (ss->ssl3.hs.msgState.buf != NULL) {
+            /* ssl3_HandleHandshake previously returned SECWouldBlock and the
+             * as-yet-unprocessed plaintext of that previous handshake record.
+             * We need to process it now before we overwrite it with the next
+             * handshake record.
+             */
+	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+        } else {
+	    /* bring in the next sslv3 record. */
+	    rv = ssl3_GatherData(ss, &ss->gs, flags);
+	    if (rv <= 0) {
+	        return rv;
+	    }
+
+	    /* decipher it, and handle it if it's a handshake. 
+	     * If it's application data, ss->gs.buf will not be empty upon return. 
+	     * If it's a change cipher spec, alert, or handshake message,
+	     * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
+	     */
+	    cText.type    = (SSL3ContentType)ss->gs.hdr[0];
+	    cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+	    cText.buf     = &ss->gs.inbuf;
+	    rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
+        }
 	if (rv < 0) {
 	    return ss->recvdCloseNotify ? 0 : rv;
 	}
 
 	/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
 	 * out of this loop early without finishing the handshake.
 	 */
 	if (ss->opt.enableFalseStart) {
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -33,17 +33,17 @@
  * 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 ***** */
-/* $Id: ssl3prot.h,v 1.19 2010/06/24 09:24:18 nelson%bolyard.com Exp $ */
+/* $Id: ssl3prot.h,v 1.20 2011/10/29 00:29:11 bsmith%mozilla.com Exp $ */
 
 #ifndef __ssl3proto_h_
 #define __ssl3proto_h_
 
 typedef uint8 SSL3Opaque;
 
 typedef uint16 SSL3ProtocolVersion;
 /* version numbers are defined in sslproto.h */
@@ -152,17 +152,18 @@ typedef enum {
     server_hello	= 2,
     new_session_ticket	= 4,
     certificate 	= 11, 
     server_key_exchange = 12,
     certificate_request	= 13, 
     server_hello_done	= 14,
     certificate_verify	= 15, 
     client_key_exchange	= 16, 
-    finished		= 20
+    finished		= 20,
+    next_proto		= 67
 } SSL3HandshakeType;
 
 typedef struct {
     uint8 empty;
 } SSL3HelloRequest;
      
 typedef struct {
     SSL3Opaque rand[SSL3_RANDOM_LENGTH];
--- a/security/nss/lib/ssl/sslcon.c
+++ b/security/nss/lib/ssl/sslcon.c
@@ -32,17 +32,17 @@
  * 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 ***** */
-/* $Id: sslcon.c,v 1.42 2011/08/01 07:08:09 kaie%kuix.de Exp $ */
+/* $Id: sslcon.c,v 1.45 2011/11/19 21:58:21 bsmith%mozilla.com Exp $ */
 
 #include "nssrenam.h"
 #include "cert.h"
 #include "secitem.h"
 #include "sechash.h"
 #include "cryptohi.h"		/* for SGN_ funcs */
 #include "keyhi.h" 		/* for SECKEY_ high level functions. */
 #include "ssl.h"
@@ -513,17 +513,16 @@ ssl2_GetSendBuffer(sslSocket *ss, unsign
 
 /* Called from:
  * ssl2_ClientSetupSessionCypher() <- ssl2_HandleServerHelloMessage()
  * ssl2_HandleRequestCertificate()     <- ssl2_HandleMessage() <- 
  					ssl_Do1stHandshake()
  * ssl2_HandleMessage()                <- ssl_Do1stHandshake()
  * ssl2_HandleServerHelloMessage() <- ssl_Do1stHandshake()
                                      after ssl2_BeginClientHandshake()
- * ssl2_RestartHandshakeAfterCertReq() <- Called from certdlgs.c in nav.
  * ssl2_HandleClientHelloMessage() <- ssl_Do1stHandshake() 
                                      after ssl2_BeginServerHandshake()
  * 
  * Acquires and releases the socket's xmitBufLock.
  */	
 int
 ssl2_SendErrorMessage(sslSocket *ss, int error)
 {
@@ -760,17 +759,16 @@ ssl2_SendCertificateRequestMessage(sslSo
     sent = (*ss->sec.send)(ss, msg, sendLen, 0);
     rv = (sent >= 0) ? SECSuccess : (SECStatus)sent;
 done:
     ssl_ReleaseXmitBufLock(ss);    /***************************************/
     return rv;
 }
 
 /* Called from ssl2_HandleRequestCertificate() <- ssl2_HandleMessage()
- *             ssl2_RestartHandshakeAfterCertReq() <- (application)
  * Acquires and releases the socket's xmitBufLock.
  */
 static int
 ssl2_SendCertificateResponseMessage(sslSocket *ss, SECItem *cert, 
                                     SECItem *encCode)
 {
     PRUint8 *msg;
     int rv, sendLen;
@@ -1172,17 +1170,16 @@ done:
 loser:
     ssl_ReleaseSpecReadLock(ss);
     return SECFailure;
 }
 
 /*
 ** Called from: ssl2_HandleServerHelloMessage,
 **              ssl2_HandleClientSessionKeyMessage,
-**              ssl2_RestartHandshakeAfterServerCert,
 **              ssl2_HandleClientHelloMessage,
 **              
 */
 static void
 ssl2_UseEncryptedSendFunc(sslSocket *ss)
 {
     ssl_GetXmitBufLock(ss);
     PORT_Assert(ss->sec.hashcx != 0);
@@ -1232,19 +1229,17 @@ ssl2_UseClearSendFunc(sslSocket *ss)
  *
  * This function is called from ssl_Do1stHandshake().  
  * The following functions put ssl_GatherRecord1stHandshake into ss->handshake:
  *	ssl2_HandleMessage
  *	ssl2_HandleVerifyMessage
  *	ssl2_HandleServerHelloMessage
  *	ssl2_BeginClientHandshake	
  *	ssl2_HandleClientSessionKeyMessage
- *	ssl2_RestartHandshakeAfterCertReq 
  *	ssl3_RestartHandshakeAfterCertReq 
- *	ssl2_RestartHandshakeAfterServerCert 
  *	ssl3_RestartHandshakeAfterServerCert 
  *	ssl2_HandleClientHelloMessage
  *	ssl2_BeginServerHandshake
  */
 SECStatus
 ssl_GatherRecord1stHandshake(sslSocket *ss)
 {
     int rv;
@@ -2227,18 +2222,16 @@ ssl2_TriggerNextMessage(sslSocket *ss)
 /* See if it's time to send our finished message, or if the handshakes are
 ** complete.  Send finished message if appropriate.
 ** Returns SECSuccess unless anything goes wrong.
 **
 ** Called from ssl2_HandleMessage,
 **             ssl2_HandleVerifyMessage 
 **             ssl2_HandleServerHelloMessage
 **             ssl2_HandleClientSessionKeyMessage
-**             ssl2_RestartHandshakeAfterCertReq
-**             ssl2_RestartHandshakeAfterServerCert
 */
 static SECStatus
 ssl2_TryToFinish(sslSocket *ss)
 {
     SECStatus        rv;
     char             e, ef;
 
     PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
@@ -2262,17 +2255,16 @@ ssl2_TryToFinish(sslSocket *ss)
 	    return SECSuccess;
 	}
     }
     return SECSuccess;
 }
 
 /*
 ** Called from ssl2_HandleRequestCertificate
-**             ssl2_RestartHandshakeAfterCertReq
 */
 static SECStatus
 ssl2_SignResponse(sslSocket *ss,
 	     SECKEYPrivateKey *key,
 	     SECItem *response)
 {
     SGNContext *     sgn = NULL;
     PRUint8 *        challenge;
@@ -2349,18 +2341,19 @@ ssl2_HandleRequestCertificate(sslSocket 
     if (!ss->getClientAuthData) {
 	SSL_TRC(7, ("%d: SSL[%d]: client doesn't support client-auth",
 		    SSL_GETPID(), ss->fd));
 	goto no_cert_error;
     }
     ret = (*ss->getClientAuthData)(ss->getClientAuthDataArg, ss->fd,
 				   NULL, &cert, &key);
     if ( ret == SECWouldBlock ) {
-	ssl_SetAlwaysBlock(ss);
-	goto done;
+	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+	ret = -1;
+	goto loser;
     }
 
     if (ret) {
 	goto no_cert_error;
     }
 
     /* check what the callback function returned */
     if ((!cert) || (!key)) {
@@ -2710,18 +2703,17 @@ ssl2_HandleMessage(sslSocket *ss)
 
   loser:
     ssl_ReleaseRecvBufLock(ss);
     return SECFailure;
 }
 
 /************************************************************************/
 
-/* Called from ssl_Do1stHandshake, after ssl2_HandleServerHelloMessage or 
-** ssl2_RestartHandshakeAfterServerCert.
+/* Called from ssl_Do1stHandshake, after ssl2_HandleServerHelloMessage.
 */
 static SECStatus
 ssl2_HandleVerifyMessage(sslSocket *ss)
 {
     PRUint8 *        data;
     SECStatus        rv;
 
     PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
@@ -2931,29 +2923,26 @@ ssl2_HandleServerHelloMessage(sslSocket 
     /* verify the server's certificate. if sidHit, don't check signatures */
     rv = (* ss->authCertificate)(ss->authCertificateArg, ss->fd, 
 				 (PRBool)(!sidHit), PR_FALSE);
     if (rv) {
 	if (ss->handleBadCert) {
 	    rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
 	    if ( rv ) {
 		if ( rv == SECWouldBlock ) {
-		    /* someone will handle this connection asynchronously*/
-
-		    SSL_DBG(("%d: SSL[%d]: go to async cert handler",
-			     SSL_GETPID(), ss->fd));
-		    ssl_ReleaseRecvBufLock(ss);
-		    ssl_SetAlwaysBlock(ss);
-		    return SECWouldBlock;
+		    SSL_DBG(("%d: SSL[%d]: SSL2 bad cert handler returned "
+			     "SECWouldBlock", SSL_GETPID(), ss->fd));
+		    PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+		    rv = SECFailure;
+		} else {
+		    /* cert is bad */
+		    SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
+			     SSL_GETPID(), ss->fd, PORT_GetError()));
 		}
-		/* cert is bad */
-		SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
-			 SSL_GETPID(), ss->fd, PORT_GetError()));
 		goto loser;
-
 	    }
 	    /* cert is good */
 	} else {
 	    SSL_DBG(("%d: SSL[%d]: server certificate is no good: error=%d",
 		     SSL_GETPID(), ss->fd, PORT_GetError()));
 	    goto loser;
 	}
     }
@@ -3326,143 +3315,16 @@ bad_client:
     PORT_SetError(SSL_ERROR_BAD_CLIENT);
     /* FALLTHROUGH */
 
 loser:
     return SECFailure;
 }
 
 /*
- * attempt to restart the handshake after asynchronously handling
- * a request for the client's certificate.
- *
- * inputs:  
- *	cert	Client cert chosen by application.
- *	key	Private key associated with cert.  
- *
- * XXX: need to make ssl2 and ssl3 versions of this function agree on whether
- *	they take the reference, or bump the ref count!
- *
- * Return value: XXX
- *
- * Caller holds 1stHandshakeLock.
- */
-int
-ssl2_RestartHandshakeAfterCertReq(sslSocket *          ss,
-				  CERTCertificate *    cert, 
-				  SECKEYPrivateKey *   key)
-{
-    int              ret;
-    SECStatus        rv          = SECSuccess;
-    SECItem          response;
-
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) 
-    	return SECFailure;
-
-    response.data = NULL;
-
-    /* generate error if no cert or key */
-    if ( ( cert == NULL ) || ( key == NULL ) ) {
-	goto no_cert;
-    }
-    
-    /* generate signed response to the challenge */
-    rv = ssl2_SignResponse(ss, key, &response);
-    if ( rv != SECSuccess ) {
-	goto no_cert;
-    }
-    
-    /* Send response message */
-    ret = ssl2_SendCertificateResponseMessage(ss, &cert->derCert, &response);
-    if (ret) {
-	goto no_cert;
-    }
-
-    /* try to finish the handshake */
-    ret = ssl2_TryToFinish(ss);
-    if (ret) {
-	goto loser;
-    }
-    
-    /* done with handshake */
-    if (ss->handshake == 0) {
-	ret = SECSuccess;
-	goto done;
-    }
-
-    /* continue handshake */
-    ssl_GetRecvBufLock(ss);
-    ss->gs.recordLen = 0;
-    ssl_ReleaseRecvBufLock(ss);
-
-    ss->handshake     = ssl_GatherRecord1stHandshake;
-    ss->nextHandshake = ssl2_HandleMessage;
-    ret = ssl2_TriggerNextMessage(ss);
-    goto done;
-    
-no_cert:
-    /* no cert - send error */
-    ret = ssl2_SendErrorMessage(ss, SSL_PE_NO_CERTIFICATE);
-    goto done;
-    
-loser:
-    ret = SECFailure;
-done:
-    /* free allocated data */
-    if ( response.data ) {
-	PORT_Free(response.data);
-    }
-    
-    return ret;
-}
-
-
-/* restart an SSL connection that we stopped to run certificate dialogs 
-** XXX	Need to document here how an application marks a cert to show that
-**	the application has accepted it (overridden CERT_VerifyCert).
- *
- * Return value: XXX
- *
- * Caller holds 1stHandshakeLock.
-*/
-int
-ssl2_RestartHandshakeAfterServerCert(sslSocket *ss)
-{
-    int rv	= SECSuccess;
-
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) 
-	return SECFailure;
-
-    /* SSL 2
-    ** At this point we have a completed session key and our session
-    ** cipher is setup and ready to go. Switch to encrypted write routine
-    ** as all future message data is to be encrypted.
-    */
-    ssl2_UseEncryptedSendFunc(ss);
-
-    rv = ssl2_TryToFinish(ss);
-    if (rv == SECSuccess && ss->handshake != NULL) {	
-	/* handshake is not yet finished. */
-
-	SSL_TRC(5, ("%d: SSL[%d]: got server-hello, required=0x%d got=0x%x",
-		SSL_GETPID(), ss->fd, ss->sec.ci.requiredElements,
-		ss->sec.ci.elements));
-
-	ssl_GetRecvBufLock(ss);
-	ss->gs.recordLen = 0;	/* mark it all used up. */
-	ssl_ReleaseRecvBufLock(ss);
-
-	ss->handshake     = ssl_GatherRecord1stHandshake;
-	ss->nextHandshake = ssl2_HandleVerifyMessage;
-    }
-
-    return rv;
-}
-
-/*
 ** Handle the initial hello message from the client
 **
 ** not static because ssl2_GatherData() tests ss->nextHandshake for this value.
 */
 SECStatus
 ssl2_HandleClientHelloMessage(sslSocket *ss)
 {
     sslSessionID    *sid;
--- a/security/nss/lib/ssl/sslerr.h
+++ b/security/nss/lib/ssl/sslerr.h
@@ -31,17 +31,17 @@
  * 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 ***** */
-/* $Id: sslerr.h,v 1.14 2011/10/05 18:07:18 emaldona%redhat.com Exp $ */
+/* $Id: sslerr.h,v 1.18 2011/11/19 21:58:21 bsmith%mozilla.com Exp $ */
 #ifndef __SSL_ERR_H_
 #define __SSL_ERR_H_
 
 
 #define SSL_ERROR_BASE				(-0x3000)
 #define SSL_ERROR_LIMIT				(SSL_ERROR_BASE + 1000)
 
 #define IS_SSL_ERROR(code) \
@@ -200,13 +200,19 @@ SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKE
 SSL_ERROR_DECOMPRESSION_FAILURE		= (SSL_ERROR_BASE + 111),
 SSL_ERROR_RENEGOTIATION_NOT_ALLOWED     = (SSL_ERROR_BASE + 112),
 SSL_ERROR_UNSAFE_NEGOTIATION            = (SSL_ERROR_BASE + 113),
 
 SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD	= (SSL_ERROR_BASE + 114),
 
 SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY  = (SSL_ERROR_BASE + 115),
 
+SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID	= (SSL_ERROR_BASE + 116),
+
+SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2 = (SSL_ERROR_BASE + 117),
+SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS = (SSL_ERROR_BASE + 118),
+SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS = (SSL_ERROR_BASE + 119),
+
 SSL_ERROR_END_OF_LIST	/* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 #endif /* __SSL_ERR_H_ */
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -34,17 +34,17 @@
  * 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 ***** */
-/* $Id: sslimpl.h,v 1.84 2011/10/22 16:45:40 emaldona%redhat.com Exp $ */
+/* $Id: sslimpl.h,v 1.90 2011/11/19 21:58:21 bsmith%mozilla.com Exp $ */
 
 #ifndef __sslimpl_h_
 #define __sslimpl_h_
 
 #ifdef DEBUG
 #undef NDEBUG
 #else
 #undef NDEBUG
@@ -308,16 +308,20 @@ typedef struct {
 
 #ifdef NSS_ENABLE_ECC
 #define ssl_V3_SUITES_IMPLEMENTED 50
 #else
 #define ssl_V3_SUITES_IMPLEMENTED 30
 #endif /* NSS_ENABLE_ECC */
 
 typedef struct sslOptionsStr {
+    /* If SSL_SetNextProtoNego has been called, then this contains the
+     * list of supported protocols. */
+    SECItem nextProtoNego;
+
     unsigned int useSecurity		: 1;  /*  1 */
     unsigned int useSocks		: 1;  /*  2 */
     unsigned int requestCertificate	: 1;  /*  3 */
     unsigned int requireCertificate	: 2;  /*  4-5 */
     unsigned int handshakeAsClient	: 1;  /*  6 */
     unsigned int handshakeAsServer	: 1;  /*  7 */
     unsigned int enableSSL2		: 1;  /*  8 */
     unsigned int enableSSL3		: 1;  /*  9 */
@@ -741,16 +745,18 @@ struct TLSExtensionDataStr {
     /* SNI Extension related data
      * Names data is not coppied from the input buffer. It can not be
      * used outside the scope where input buffer is defined and that
      * is beyond ssl3_HandleClientHello function. */
     SECItem *sniNameArr;
     PRUint32 sniNameArrSize;
 };
 
+typedef SECStatus (*sslRestartTarget)(sslSocket *);
+
 /*
 ** This is the "hs" member of the "ssl3" struct.
 ** This entire struct is protected by ssl3HandshakeLock
 */
 typedef struct SSL3HandshakeStateStr {
     SSL3Random            server_random;
     SSL3Random            client_random;
     SSL3WaitState         ws;
@@ -766,32 +772,37 @@ const ssl3CipherSuiteDef *suite_def;
                                /* partial handshake message from record layer */
     unsigned int          header_bytes; 
                                /* number of bytes consumed from handshake */
                                /* message for message type and header length */
     SSL3HandshakeType     msg_type;
     unsigned long         msg_len;
     SECItem               ca_list;     /* used only by client */
     PRBool                isResuming;  /* are we resuming a session */
-    PRBool                rehandshake; /* immediately start another handshake 
-                                        * when this one finishes */
     PRBool                usedStepDownKey;  /* we did a server key exchange. */
     PRBool                sendingSCSV; /* instead of empty RI */
     sslBuffer             msgState;    /* current state for handshake messages*/
                                        /* protected by recvBufLock */
     sslBuffer             messages;    /* Accumulated handshake messages */
     PRUint16              finishedBytes; /* size of single finished below */
     union {
 	TLSFinished       tFinished[2]; /* client, then server */
 	SSL3Hashes        sFinished[2];
 	SSL3Opaque        data[72];
     }                     finishedMsgs;
 #ifdef NSS_ENABLE_ECC
     PRUint32              negotiatedECCurves; /* bit mask */
 #endif /* NSS_ENABLE_ECC */
+
+    PRBool                authCertificatePending;
+    /* Which function should SSL_RestartHandshake* call if we're blocked?
+     * One of NULL, ssl3_SendClientSecondRound, or ssl3_FinishHandshake. */
+    sslRestartTarget      restartTarget;
+    /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
+    PRBool                cacheSID; 
 } SSL3HandshakeState;
 
 
 
 /*
 ** This is the "ssl3" struct, as in "ss->ssl3".
 ** note:
 ** usually,   crSpec == cwSpec and prSpec == pwSpec.  
@@ -823,16 +834,22 @@ struct ssl3StateStr {
 			    /* These are used to keep track of the peer CA */
     void *               peerCertChain;     
 			    /* chain while we are trying to validate it.   */
     CERTDistNames *      ca_list; 
 			    /* used by server.  trusted CAs for this socket. */
     PRBool               initialized;
     SSL3HandshakeState   hs;
     ssl3CipherSpec       specs[2];	/* one is current, one is pending. */
+
+    /* In a client: if the server supports Next Protocol Negotiation, then
+     * this is the protocol that was negotiated.
+     */
+    SECItem		 nextProto;
+    SSLNextProtoState    nextProtoState;
 };
 
 typedef struct {
     SSL3ContentType      type;
     SSL3ProtocolVersion  version;
     sslBuffer *          buf;
 } SSL3Ciphertext;
 
@@ -1054,16 +1071,18 @@ const unsigned char *  preferredCipher;
     void                     *getClientAuthDataArg;
     SSLSNISocketConfig        sniSocketConfig;
     void                     *sniSocketConfigArg;
     SSLBadCertHandler         handleBadCert;
     void                     *badCertArg;
     SSLHandshakeCallback      handshakeCallback;
     void                     *handshakeCallbackData;
     void                     *pkcs11PinArg;
+    SSLNextProtoCallback      nextProtoCallback;
+    void                     *nextProtoArg;
 
     PRIntervalTime            rTimeout; /* timeout for NSPR I/O */
     PRIntervalTime            wTimeout; /* timeout for NSPR I/O */
     PRIntervalTime            cTimeout; /* timeout for NSPR I/O */
 
     PZLock *      recvLock;	/* lock against multiple reader threads. */
     PZLock *      sendLock;	/* lock against multiple sender threads. */
 
@@ -1133,17 +1152,16 @@ const unsigned char *  preferredCipher;
 extern NSSRWLock *             ssl_global_data_lock;
 extern char                    ssl_debug;
 extern char                    ssl_trace;
 extern FILE *                  ssl_trace_iob;
 extern FILE *                  ssl_keylog_iob;
 extern CERTDistNames *         ssl3_server_ca_list;
 extern PRUint32                ssl_sid_timeout;
 extern PRUint32                ssl3_sid_timeout;
-extern PRBool                  ssl3_global_policy_some_restricted;
 
 extern const char * const      ssl_cipherName[];
 extern const char * const      ssl3_cipherName[];
 
 extern sslSessionIDLookupFunc  ssl_sid_lookup;
 extern sslSessionIDCacheFunc   ssl_sid_cache;
 extern sslSessionIDUncacheFunc ssl_sid_uncache;
 
@@ -1247,17 +1265,17 @@ extern void      ssl_FreeSID(sslSessionI
 
 extern int       ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in,
 				          int len, int flags);
 
 extern PRBool    ssl_FdIsBlocking(PRFileDesc *fd);
 
 extern PRBool    ssl_SocketIsBlocking(sslSocket *ss);
 
-extern void      ssl_SetAlwaysBlock(sslSocket *ss);
+extern void      ssl3_SetAlwaysBlock(sslSocket *ss);
 
 extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
 
 extern PRBool    ssl3_CanFalseStart(sslSocket *ss);
 
 #define SSL_LOCK_READER(ss)		if (ss->recvLock) PZ_Lock(ss->recvLock)
 #define SSL_UNLOCK_READER(ss)		if (ss->recvLock) PZ_Unlock(ss->recvLock)
 #define SSL_LOCK_WRITER(ss)		if (ss->sendLock) PZ_Lock(ss->sendLock)
@@ -1326,37 +1344,26 @@ extern SECStatus ssl3_KeyAndMacDeriveByp
 		    PRBool isTLS, PRBool isExport);
 extern  SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec,
 		    const unsigned char * cr, const unsigned char * sr,
 		    const SECItem * pms, PRBool isTLS, PRBool isRSA);
 
 /* These functions are called from secnav, even though they're "private". */
 
 extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
-extern int SSL_RestartHandshakeAfterServerCert(struct sslSocketStr *ss);
 extern int SSL_RestartHandshakeAfterCertReq(struct sslSocketStr *ss,
 					    CERTCertificate *cert,
 					    SECKEYPrivateKey *key,
 					    CERTCertificateList *certChain);
 extern sslSocket *ssl_FindSocket(PRFileDesc *fd);
 extern void ssl_FreeSocket(struct sslSocketStr *ssl);
 extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
 				SSL3AlertDescription desc);
 
-extern int ssl2_RestartHandshakeAfterCertReq(sslSocket *          ss,
-					     CERTCertificate *    cert, 
-					     SECKEYPrivateKey *   key);
-
-extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket *    ss,
-					     CERTCertificate *    cert, 
-					     SECKEYPrivateKey *   key,
-					     CERTCertificateList *certChain);
-
-extern int ssl2_RestartHandshakeAfterServerCert(sslSocket *ss);
-extern int ssl3_RestartHandshakeAfterServerCert(sslSocket *ss);
+extern SECStatus ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss);
 
 /*
  * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
  */
 extern SECStatus ssl3_HandleV2ClientHello(
     sslSocket *ss, unsigned char *buffer, int length);
 extern SECStatus ssl3_StartHandshakeHash(
     sslSocket *ss, unsigned char *buf, int length);
@@ -1564,16 +1571,19 @@ extern PRBool ssl_GetSessionTicketKeysPK
 			SECKEYPublicKey *svrPubKey, void *pwArg,
 			unsigned char *keyName, PK11SymKey **aesKey,
 			PK11SymKey **macKey);
 
 /* Tell clients to consider tickets valid for this long. */
 #define TLS_EX_SESS_TICKET_LIFETIME_HINT    (2 * 24 * 60 * 60) /* 2 days */
 #define TLS_EX_SESS_TICKET_VERSION          (0x0100)
 
+extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data,
+					    unsigned int length);
+
 /* Construct a new NSPR socket for the app to use */
 extern PRFileDesc *ssl_NewPRSocket(sslSocket *ss, PRFileDesc *fd);
 extern void ssl_FreePRSocket(PRFileDesc *fd);
 
 /* Internal config function so SSL2 can initialize the present state of 
  * various ciphers */
 extern int ssl3_config_match_init(sslSocket *);
 
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -32,17 +32,17 @@
  * 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 ***** */
-/* $Id: sslsecur.c,v 1.49 2011/04/08 05:37:44 wtc%google.com Exp $ */
+/* $Id: sslsecur.c,v 1.53 2011/11/19 21:58:21 bsmith%mozilla.com Exp $ */
 #include "cert.h"
 #include "secitem.h"
 #include "keyhi.h"
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "secoid.h"	/* for SECOID_GetALgorithmTag */
 #include "pk11func.h"	/* for PK11_GenerateRandom */
@@ -168,30 +168,30 @@ ssl_Do1stHandshake(sslSocket *ss)
     return rv;
 }
 
 /*
  * Handshake function that blocks.  Used to force a
  * retry on a connection on the next read/write.
  */
 static SECStatus
-AlwaysBlock(sslSocket *ss)
+ssl3_AlwaysBlock(sslSocket *ss)
 {
     PORT_SetError(PR_WOULD_BLOCK_ERROR);	/* perhaps redundant. */
     return SECWouldBlock;
 }
 
 /*
  * set the initial handshake state machine to block
  */
 void
-ssl_SetAlwaysBlock(sslSocket *ss)
+ssl3_SetAlwaysBlock(sslSocket *ss)
 {
     if (!ss->firstHsDone) {
-	ss->handshake = AlwaysBlock;
+	ss->handshake = ssl3_AlwaysBlock;
 	ss->nextHandshake = 0;
     }
 }
 
 static SECStatus 
 ssl_SetTimeout(PRFileDesc *fd, PRIntervalTime timeout)
 {
     sslSocket *ss;
@@ -387,16 +387,28 @@ SSL_ForceHandshake(PRFileDesc *fd)
 		 SSL_GETPID(), fd));
 	return rv;
     }
 
     /* Don't waste my time */
     if (!ss->opt.useSecurity) 
     	return SECSuccess;
 
+    if (!ssl_SocketIsBlocking(ss)) {
+	ssl_GetXmitBufLock(ss);
+	if (ss->pendingBuf.len != 0) {
+	    rv = ssl_SendSavedWriteData(ss);
+	    if ((rv < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
+		ssl_ReleaseXmitBufLock(ss);
+		return SECFailure;
+	    }
+	}
+	ssl_ReleaseXmitBufLock(ss);
+    }
+
     ssl_Get1stHandshakeLock(ss);
 
     if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
 	int gatherResult;
 
     	ssl_GetRecvBufLock(ss);
 	gatherResult = ssl3_GatherCompleteHandshake(ss, 0);
 	ssl_ReleaseRecvBufLock(ss);
@@ -1136,17 +1148,16 @@ ssl_SecureRecv(sslSocket *ss, unsigned c
     if (!ssl_SocketIsBlocking(ss) && !ss->opt.fdx) {
 	ssl_GetXmitBufLock(ss);
 	if (ss->pendingBuf.len != 0) {
 	    rv = ssl_SendSavedWriteData(ss);
 	    if ((rv < 0) && (PORT_GetError() != PR_WOULD_BLOCK_ERROR)) {
 		ssl_ReleaseXmitBufLock(ss);
 		return SECFailure;
 	    }
-	    /* XXX short write? */
 	}
 	ssl_ReleaseXmitBufLock(ss);
     }
     
     rv = 0;
     /* If any of these is non-zero, the initial handshake is not done. */
     if (!ss->firstHsDone) {
 	ssl_Get1stHandshakeLock(ss);
@@ -1447,84 +1458,66 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
     if (!dbHandle) {
     	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	return SECFailure;
     }
     ss->dbHandle = dbHandle;
     return SECSuccess;
 }
 
-/*
- * attempt to restart the handshake after asynchronously handling
- * a request for the client's certificate.
- *
- * inputs:  
- *	cert	Client cert chosen by application.
- *		Note: ssl takes this reference, and does not bump the 
- *		reference count.  The caller should drop its reference
- *		without calling CERT_DestroyCert after calling this function.
- *
- *	key	Private key associated with cert.  This function makes a 
- *		copy of the private key, so the caller remains responsible 
- *		for destroying its copy after this function returns.
- *
- *	certChain  Chain of signers for cert.  
- *		Note: ssl takes this reference, and does not copy the chain.
- *		The caller should drop its reference without destroying the 
- *		chain.  SSL will free the chain when it is done with it.
- *
- * Return value: XXX
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
  */
 int
 SSL_RestartHandshakeAfterCertReq(sslSocket *         ss,
 				CERTCertificate *    cert, 
 				SECKEYPrivateKey *   key,
 				CERTCertificateList *certChain)
 {
-    int              ret;
-
-    ssl_Get1stHandshakeLock(ss);   /************************************/
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    return -1;
+}
 
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
-	ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
-    } else {
-    	ret = ssl2_RestartHandshakeAfterCertReq(ss, cert, key);
-    }
-
-    ssl_Release1stHandshakeLock(ss);  /************************************/
-    return ret;
+/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
+ * this implementation exists to maintain link-time compatibility.
+ */
+int
+SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
+{
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    return -1;
 }
 
+/* See documentation in ssl.h */
+SECStatus
+SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd)
+{
+    SECStatus rv = SECSuccess;
+    sslSocket *ss = ssl_FindSocket(fd);
 
-/* restart an SSL connection that we stopped to run certificate dialogs 
-** XXX	Need to document here how an application marks a cert to show that
-**	the application has accepted it (overridden CERT_VerifyCert).
- *
- * XXX This code only works on the initial handshake on a connection, XXX
- *     It does not work on a subsequent handshake (redo).
- *
- * Return value: XXX
-*/
-int
-SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
-{
-    int rv	= SECSuccess;
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    ssl_Get1stHandshakeLock(ss);
 
-    ssl_Get1stHandshakeLock(ss); 
-
-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
-	rv = ssl3_RestartHandshakeAfterServerCert(ss);
+    if (!ss->ssl3.initialized) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	rv = SECFailure;
+    } else if (ss->version < SSL_LIBRARY_VERSION_3_0) {
+	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+	rv = SECFailure;
     } else {
-	rv = ssl2_RestartHandshakeAfterServerCert(ss);
+	rv = ssl3_RestartHandshakeAfterAuthCertificate(ss);
     }
 
     ssl_Release1stHandshakeLock(ss);
+
     return rv;
 }
 
 /* For more info see ssl.h */
 SECStatus 
 SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
                         void *arg)
 {
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -35,17 +35,17 @@
  * 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 ***** */
-/* $Id: sslsock.c,v 1.75 2011/10/22 16:45:40 emaldona%redhat.com Exp $ */
+/* $Id: sslsock.c,v 1.80 2011/11/17 00:20:22 bsmith%mozilla.com Exp $ */
 #include "seccomon.h"
 #include "cert.h"
 #include "keyhi.h"
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "nspr.h"
 #include "private/pprio.h"
@@ -158,16 +158,17 @@ static const sslSocketOps ssl_secure_ops
     ssl_DefGetpeername,
     ssl_DefGetsockname
 };
 
 /*
 ** default settings for socket enables
 */
 static sslOptions ssl_defaults = {
+    { siBuffer, NULL, 0 }, /* nextProtoNego */
     PR_TRUE, 	/* useSecurity        */
     PR_FALSE,	/* useSocks           */
     PR_FALSE,	/* requestCertificate */
     2,	        /* requireCertificate */
     PR_FALSE,	/* handshakeAsClient  */
     PR_FALSE,	/* handshakeAsServer  */
     PR_FALSE,	/* enableSSL2         */ /* now defaults to off in NSS 3.13 */
     PR_TRUE,	/* enableSSL3         */
@@ -435,16 +436,17 @@ ssl_DestroySocketContents(sslSocket *ss)
     if (ss->stepDownKeyPair) {
 	ssl3_FreeKeyPair(ss->stepDownKeyPair);
 	ss->stepDownKeyPair = NULL;
     }
     if (ss->ephemeralECDHKeyPair) {
 	ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair);
 	ss->ephemeralECDHKeyPair = NULL;
     }
+    SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
     PORT_Assert(!ss->xtnData.sniNameArr);
     if (ss->xtnData.sniNameArr) {
         PORT_Free(ss->xtnData.sniNameArr);
         ss->xtnData.sniNameArr = NULL;
     }
 }
 
 /*
@@ -1207,57 +1209,37 @@ SSL_CipherPrefGet(PRFileDesc *fd, PRInt3
 	rv = ssl3_CipherPrefGet(ss, (ssl3CipherSuite)which, enabled);
     }
     return rv;
 }
 
 SECStatus
 NSS_SetDomesticPolicy(void)
 {
-#ifndef EXPORT_VERSION
     SECStatus      status = SECSuccess;
     cipherPolicy * policy;
 
     for (policy = ssl_ciphers; policy->cipher != 0; ++policy) {
 	status = SSL_SetPolicy(policy->cipher, SSL_ALLOWED);
 	if (status != SECSuccess)
 	    break;
     }
     return status;
-#else
-    return NSS_SetExportPolicy();
-#endif
 }
 
 SECStatus
 NSS_SetExportPolicy(void)
 {
-    SECStatus      status = SECSuccess;
-    cipherPolicy * policy;
-
-    for (policy = ssl_ciphers; policy->cipher != 0; ++policy) {
-	status = SSL_SetPolicy(policy->cipher, policy->export);
-	if (status != SECSuccess)
-	    break;
-    }
-    return status;
+    return NSS_SetDomesticPolicy();
 }
 
 SECStatus
 NSS_SetFrancePolicy(void)
 {
-    SECStatus      status = SECSuccess;
-    cipherPolicy * policy;
-
-    for (policy = ssl_ciphers; policy->cipher != 0; ++policy) {
-	status = SSL_SetPolicy(policy->cipher, policy->france);
-	if (status != SECSuccess)
-	    break;
-    }
-    return status;
+    return NSS_SetDomesticPolicy();
 }
 
 
 
 /* LOCKS ??? XXX */
 PRFileDesc *
 SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
 {
@@ -1296,16 +1278,155 @@ SSL_ImportFD(PRFileDesc *model, PRFileDe
 #endif
     ns = ssl_FindSocket(fd);
     PORT_Assert(ns);
     if (ns)
 	ns->TCPconnected = (PR_SUCCESS == ssl_DefGetpeername(ns, &addr));
     return fd;
 }
 
+SECStatus
+SSL_SetNextProtoCallback(PRFileDesc *fd, SSLNextProtoCallback callback,
+			 void *arg)
+{
+    sslSocket *ss = ssl_FindSocket(fd);
+
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoCallback", SSL_GETPID(),
+		 fd));
+	return SECFailure;
+    }
+
+    ssl_GetSSL3HandshakeLock(ss);
+    ss->nextProtoCallback = callback;
+    ss->nextProtoArg = arg;
+    ssl_ReleaseSSL3HandshakeLock(ss);
+
+    return SECSuccess;
+}
+
+/* NextProtoStandardCallback is set as an NPN callback for the case when
+ * SSL_SetNextProtoNego is used.
+ */
+static SECStatus
+ssl_NextProtoNegoCallback(void *arg, PRFileDesc *fd,
+			  const unsigned char *protos, unsigned int protos_len,
+			  unsigned char *protoOut, unsigned int *protoOutLen,
+			  unsigned int protoMaxLen)
+{
+    unsigned int i, j;
+    const unsigned char *result;
+    sslSocket *ss = ssl_FindSocket(fd);
+
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in ssl_NextProtoNegoCallback",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    if (protos_len == 0) {
+	/* The server supports the extension, but doesn't have any protocols
+	 * configured. In this case we request our favoured protocol. */
+	goto pick_first;
+    }
+
+    /* For each protocol in server preference, see if we support it. */
+    for (i = 0; i < protos_len; ) {
+	for (j = 0; j < ss->opt.nextProtoNego.len; ) {
+	    if (protos[i] == ss->opt.nextProtoNego.data[j] &&
+		PORT_Memcmp(&protos[i+1], &ss->opt.nextProtoNego.data[j+1],
+			     protos[i]) == 0) {
+		/* We found a match. */
+		ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED;
+		result = &protos[i];
+		goto found;
+	    }
+	    j += 1 + (unsigned int)ss->opt.nextProtoNego.data[j];
+	}
+	i += 1 + (unsigned int)protos[i];
+    }
+
+pick_first:
+    ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP;
+    result = ss->opt.nextProtoNego.data;
+
+found:
+    *protoOutLen = result[0];
+    if (protoMaxLen < result[0]) {
+	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+	return SECFailure;
+    }
+    memcpy(protoOut, result + 1, result[0]);
+    return SECSuccess;
+}
+
+SECStatus
+SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data,
+		     unsigned int length)
+{
+    sslSocket *ss;
+    SECStatus rv;
+    SECItem dataItem = { siBuffer, (unsigned char *) data, length };
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess)
+	return SECFailure;
+
+    ssl_GetSSL3HandshakeLock(ss);
+    SECITEM_FreeItem(&ss->opt.nextProtoNego, PR_FALSE);
+    rv = SECITEM_CopyItem(NULL, &ss->opt.nextProtoNego, &dataItem);
+    ssl_ReleaseSSL3HandshakeLock(ss);
+
+    if (rv != SECSuccess)
+	return rv;
+
+    return SSL_SetNextProtoCallback(fd, ssl_NextProtoNegoCallback, NULL);
+}
+
+SECStatus
+SSL_GetNextProto(PRFileDesc *fd, SSLNextProtoState *state, unsigned char *buf,
+		 unsigned int *bufLen, unsigned int bufLenMax)
+{
+    sslSocket *ss = ssl_FindSocket(fd);
+
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNextProto", SSL_GETPID(),
+		 fd));
+	return SECFailure;
+    }
+
+    if (!state || !buf || !bufLen) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    *state = ss->ssl3.nextProtoState;
+
+    if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
+	ss->ssl3.nextProto.data) {
+	*bufLen = ss->ssl3.nextProto.len;
+	if (*bufLen > bufLenMax) {
+	    PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+	    *bufLen = 0;
+	    return SECFailure;
+	}
+	PORT_Memcpy(buf, ss->ssl3.nextProto.data, ss->ssl3.nextProto.len);
+    } else {
+	*bufLen = 0;
+    }
+
+    return SECSuccess;
+}
+
 PRFileDesc *
 SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
 {
     sslSocket * sm = NULL, *ss = NULL;
     int i;
     sslServerCerts * mc = NULL;
     sslServerCerts * sc = NULL;
 
--- a/security/nss/lib/ssl/sslt.h
+++ b/security/nss/lib/ssl/sslt.h
@@ -32,17 +32,17 @@
  * 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 ***** */
-/* $Id: sslt.h,v 1.16 2010/02/04 03:21:11 wtc%google.com Exp $ */
+/* $Id: sslt.h,v 1.17 2011/10/29 00:29:11 bsmith%mozilla.com Exp $ */
 
 #ifndef __sslt_h_
 #define __sslt_h_
 
 #include "prtypes.h"
 
 typedef struct SSL3StatisticsStr {
     /* statistics from ssl3_SendClientHello (sch) */
@@ -198,14 +198,15 @@ typedef enum {
 /* Update SSL_MAX_EXTENSIONS whenever a new extension type is added. */
 typedef enum {
     ssl_server_name_xtn              = 0,
 #ifdef NSS_ENABLE_ECC
     ssl_elliptic_curves_xtn          = 10,
     ssl_ec_point_formats_xtn         = 11,
 #endif
     ssl_session_ticket_xtn           = 35,
+    ssl_next_proto_neg_xtn           = 13172,
     ssl_renegotiation_info_xtn       = 0xff01	/* experimental number */
 } SSLExtensionType;
 
-#define SSL_MAX_EXTENSIONS             5
+#define SSL_MAX_EXTENSIONS             6
 
 #endif /* __sslt_h_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -46,22 +46,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION  "3.13.1.0"
+#define NSSUTIL_VERSION  "3.13.2.0 Beta"
 #define NSSUTIL_VMAJOR   3
 #define NSSUTIL_VMINOR   13
-#define NSSUTIL_VPATCH   1
+#define NSSUTIL_VPATCH   2
 #define NSSUTIL_VBUILD   0
-#define NSSUTIL_BETA     PR_FALSE
+#define NSSUTIL_BETA     PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/security/nss/lib/util/pkcs11n.h
+++ b/security/nss/lib/util/pkcs11n.h
@@ -34,17 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _PKCS11N_H_
 #define _PKCS11N_H_
 
 #ifdef DEBUG
-static const char CKT_CVS_ID[] = "@(#) $RCSfile: pkcs11n.h,v $ $Revision: 1.23 $ $Date: 2011/09/14 01:21:10 $";
+static const char CKT_CVS_ID[] = "@(#) $RCSfile: pkcs11n.h,v $ $Revision: 1.27 $ $Date: 2011/11/24 12:26:35 $";
 #endif /* DEBUG */
 
 /*
  * pkcs11n.h
  *
  * This file contains the NSS-specific type definitions for Cryptoki
  * (PKCS#11).
  */
@@ -157,17 +157,16 @@ static const char CKT_CVS_ID[] = "@(#) $
 #define CKA_TRUST_IPSEC_USER            (CKA_TRUST + 14)
 #define CKA_TRUST_TIME_STAMPING         (CKA_TRUST + 15)
 #define CKA_TRUST_STEP_UP_APPROVED      (CKA_TRUST + 16)
 
 #define CKA_CERT_SHA1_HASH	        (CKA_TRUST + 100)
 #define CKA_CERT_MD5_HASH		(CKA_TRUST + 101)
 
 /* NSS trust stuff */
-/* XXX fgmr new ones here-- step-up, etc. */
 
 /* HISTORICAL: define used to pass in the database key for DSA private keys */
 #define CKA_NETSCAPE_DB                 0xD5A0DB00L
 #define CKA_NETSCAPE_TRUST              0x80000001L
 
 /* FAKE PKCS #11 defines */
 #define CKM_FAKE_RANDOM       0x80000efeUL
 #define CKM_INVALID_MECHANISM 0xffffffffUL
@@ -341,33 +340,33 @@ typedef CK_ULONG          CK_TRUST;
 #define CKT_NSS_VALID_DELEGATOR    (CKT_NSS + 11)
 
 
 /*
  * old definitions. They still exist, but the plain meaning of the
  * labels have never been accurate to what was really implemented.
  * The new labels correctly reflect what the values effectively mean.
  */
-#if __GNUC__ > 3
+#if defined(__GNUC__) && (__GNUC__ > 3)
 /* make GCC warn when we use these #defines */
 /*
  *  This is really painful because GCC doesn't allow us to mark random
  *  #defines as deprecated. We can only mark the following:
  *      functions, variables, and types.
  *  const variables will create extra storage for everyone including this
  *       header file, so it's undesirable.
  *  functions could be inlined to prevent storage creation, but will fail
  *       when constant values are expected (like switch statements).
  *  enum types do not seem to pay attention to the deprecated attribute.
  *
  *  That leaves typedefs. We declare new types that we then deprecate, then
  *  cast the resulting value to the deprecated type in the #define, thus
  *  producting the warning when the #define is used.
  */
-#if (__GNUC__  == 4) && (__GNUC_MINOR < 5)
+#if (__GNUC__  == 4) && (__GNUC_MINOR__ < 5)
 /* The mac doesn't like the friendlier deprecate messages. I'm assuming this
  * is a gcc version issue rather than mac or ppc specific */
 typedef CK_TRUST __CKT_NSS_UNTRUSTED __attribute__((deprecated));
 typedef CK_TRUST __CKT_NSS_VALID __attribute__ ((deprecated));
 typedef CK_TRUST __CKT_NSS_MUST_VERIFY __attribute__((deprecated));
 #else
 /* when possible, get a full deprecation warning. This works on gcc 4.5
  * it may work on earlier versions of gcc */
--- a/security/nss/lib/util/secder.h
+++ b/security/nss/lib/util/secder.h
@@ -38,17 +38,17 @@
 #define _SECDER_H_
 
 #include "utilrename.h"
 
 /*
  * secder.h - public data structures and prototypes for the DER encoding and
  *	      decoding utilities library
  *
- * $Id: secder.h,v 1.13 2008/06/18 01:04:23 wtc%google.com Exp $
+ * $Id: secder.h,v 1.15 2011/11/16 19:12:36 kaie%kuix.de Exp $
  */
 
 #if defined(_WIN32_WCE)
 #else
 #include <time.h>
 #endif
 
 #include "plarena.h"
--- a/security/nss/lib/util/secoid.h
+++ b/security/nss/lib/util/secoid.h
@@ -37,17 +37,17 @@
 #ifndef _SECOID_H_
 #define _SECOID_H_
 
 #include "utilrename.h"
 
 /*
  * secoid.h - public data structures and prototypes for ASN.1 OID functions
  *
- * $Id: secoid.h,v 1.14 2009/11/11 23:24:33 alexei.volkov.bugs%sun.com Exp $
+ * $Id: secoid.h,v 1.16 2011/11/16 19:12:36 kaie%kuix.de Exp $
  */
 
 #include "plarena.h"
 
 #include "seccomon.h"
 #include "secoidt.h"
 #include "secasn1t.h"
 
--- a/security/nss/tests/pkits/pkits.sh
+++ b/security/nss/tests/pkits/pkits.sh
@@ -122,17 +122,17 @@ pkits_init()
   echo "crls" $crls
 
   echo nss > ${PKITSdb}/pw
   ${BINDIR}/certutil -N -d ${PKITSdb} -f ${PKITSdb}/pw
 
   ${BINDIR}/certutil -A -n TrustAnchorRootCertificate -t "C,C,C" -i \
       $certs/TrustAnchorRootCertificate.crt -d $PKITSdb
   if [ -z "$NSS_NO_PKITS_CRLS" ]; then
-    ${BINDIR}/crlutil -I -i $crls/TrustAnchorRootCRL.crl -d ${PKITSdb}
+    ${BINDIR}/crlutil -I -i $crls/TrustAnchorRootCRL.crl -d ${PKITSdb} -f ${PKITSdb}/pw
   else
     html  "<H3>NO CRLs are being used.</H3>"
     pkits_log "NO CRLs are being used."
   fi
 
   cp ${PKITSdb}/* ${PKITSbkp}
 
   KNOWN_BUG=
@@ -229,18 +229,18 @@ pkitsn()
 
 ################################ crlImport #############################
 # local shell function to import a CRL, calls crlutil -I -i, writes 
 # action and options to stdout
 ########################################################################
 crlImport()
 {
   if [ -z "$NSS_NO_PKITS_CRLS" ]; then
-    echo "crlutil -d $PKITSdb -I -i $crls/$*"
-    ${BINDIR}/crlutil -d ${PKITSdb} -I -i $crls/$* > ${PKITSDIR}/cmdout.txt 2>&1
+    echo "crlutil -d $PKITSdb -I -f ${PKITSdb}/pw -i $crls/$*"
+    ${BINDIR}/crlutil -d ${PKITSdb} -I -f ${PKITSdb}/pw -i $crls/$* > ${PKITSDIR}/cmdout.txt 2>&1
     RET=$?
     cat ${PKITSDIR}/cmdout.txt
 
     if [ "$RET" -ne 0 ]; then
         html_failed "${VFY_ACTION} ($RET) "
         pkits_log "ERROR: ${VFY_ACTION} failed $RET"
     fi
   fi
@@ -249,18 +249,18 @@ crlImport()
 ################################ crlImportn #############################
 # local shell function to import an incorrect CRL, calls crlutil -I -i, 
 # writes action and options to stdout
 ########################################################################
 crlImportn()
 {
   RET=0
   if [ -z "$NSS_NO_PKITS_CRLS" ]; then
-    echo "crlutil -d $PKITSdb -I -i $crls/$*"
-    ${BINDIR}/crlutil -d ${PKITSdb} -I -i $crls/$* > ${PKITSDIR}/cmdout.txt 2>&1
+    echo "crlutil -d $PKITSdb -I -f ${PKITSdb}/pw -i $crls/$*"
+    ${BINDIR}/crlutil -d ${PKITSdb} -I -f ${PKITSdb}/pw -i $crls/$* > ${PKITSDIR}/cmdout.txt 2>&1
     RET=$?
     cat ${PKITSDIR}/cmdout.txt
 
     if [ "$RET" -eq 0 ]; then
         html_failed "${VFY_ACTION} ($RET) "
         pkits_log "ERROR: ${VFY_ACTION} failed $RET"
     else
         html_passed "${VFY_ACTION} ($RET) "
--- a/security/nss/tests/ssl/ssl.sh
+++ b/security/nss/tests/ssl/ssl.sh
@@ -303,16 +303,26 @@ ssl_cov()
                
   exec < ${SSLCOV}
   while read ectype tls param testname
   do
       echo "${testname}" | grep "EXPORT" > /dev/null 
       EXP=$?
       echo "${testname}" | grep "SSL2" > /dev/null
       SSL2=$?
+
+      if [ "${SSL2}" -eq 0 ] ; then
+          # We cannot use asynchronous cert verification with SSL2
+          SSL2_FLAGS=-O
+      else
+          # Do not enable SSL2 for non-SSL2-specific tests. SSL2 is disabled by
+          # default in libssl but it is enabled by default in tstclnt; we want
+          # to test the libssl default whenever possible.
+          SSL2_FLAGS=-2
+      fi
       
       if [ "$NORM_EXT" = "Extended Test" -a "${SSL2}" -eq 0 ] ; then
           echo "$SCRIPTNAME: skipping  $testname for $NORM_EXT"
       elif [ "$ectype" = "ECC" -a -z "$NSS_ENABLE_ECC" ] ; then
           echo "$SCRIPTNAME: skipping  $testname (ECC only)"
       elif [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] && [ "$SSL2" -eq 0 -o "$EXP" -eq 0 ] ; then
           echo "$SCRIPTNAME: skipping  $testname (non-FIPS only)"
       elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
@@ -345,21 +355,21 @@ ssl_cov()
               is_selfserv_alive
             else
               kill_selfserv
               start_selfserv
               mixed=0
             fi
           fi
 
-          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} \\"
+          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} \\"
           echo "        -f -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE}"
 
           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
-          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} -f \
+          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} -f \
                   -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE} \
                   >${TMP}/$HOST.tmp.$$  2>&1
           ret=$?
           cat ${TMP}/$HOST.tmp.$$ 
           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
           html_msg $ret 0 "${testname}" \
                    "produced a returncode of $ret, expected is 0"
       fi
--- a/security/patches/README
+++ b/security/patches/README
@@ -1,2 +1,7 @@
 This directory contains patches that were added locally
 on top of the NSS release.
+
+bug-542832-ssl-restart-4.patch and bug-542832-ssl-restart-tstclnt-4.patch were
+added so that we could test the new PSM SSL threading code (bug 674147) and
+SPDY (bug 528288). These patches will be removed when the NSS 3.13.2 release
+that includes them is imported into mozilla-central.
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-542832-ssl-restart-4.patch
@@ -0,0 +1,1076 @@
+Index: mozilla/security/nss/lib/ssl/SSLerrs.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/SSLerrs.h,v
+retrieving revision 1.15
+diff -u -8 -p -r1.15 SSLerrs.h
+--- mozilla/security/nss/lib/ssl/SSLerrs.h	11 Nov 2011 19:06:51 -0000	1.15
++++ mozilla/security/nss/lib/ssl/SSLerrs.h	16 Nov 2011 08:21:01 -0000
+@@ -406,8 +406,14 @@ ER3(SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED
+ ER3(SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY,    (SSL_ERROR_BASE + 115),
+ "SSL received a weak ephemeral Diffie-Hellman key in Server Key Exchange handshake message.")
+ 
+ ER3(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID,      (SSL_ERROR_BASE + 116),
+ "SSL received invalid NPN extension data.")
+ 
+ ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2,  (SSL_ERROR_BASE + 117),
+ "SSL feature not supported for SSL 2.0 connections.")
++
++ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS, (SSL_ERROR_BASE + 118),
++"SSL feature not supported for servers.")
++
++ER3(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS, (SSL_ERROR_BASE + 119),
++"SSL feature not supported for clients.")
+Index: mozilla/security/nss/lib/ssl/ssl.def
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.def,v
+retrieving revision 1.27
+diff -u -8 -p -r1.27 ssl.def
+--- mozilla/security/nss/lib/ssl/ssl.def	29 Oct 2011 00:29:11 -0000	1.27
++++ mozilla/security/nss/lib/ssl/ssl.def	16 Nov 2011 08:21:01 -0000
+@@ -164,11 +164,12 @@ NSSSSL_GetVersion;
+ ;+    local:
+ ;+       *;
+ ;+};
+ ;+NSS_3.13.2 {    # NSS 3.13.2 release
+ ;+    global:
+ SSL_SetNextProtoCallback;
+ SSL_SetNextProtoNego;
+ SSL_GetNextProto;
++SSL_RestartHandshakeAfterAuthCertificate;
+ ;+    local:
+ ;+       *;
+ ;+};
+Index: mozilla/security/nss/lib/ssl/ssl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.h,v
+retrieving revision 1.45
+diff -u -8 -p -r1.45 ssl.h
+--- mozilla/security/nss/lib/ssl/ssl.h	29 Oct 2011 00:29:11 -0000	1.45
++++ mozilla/security/nss/lib/ssl/ssl.h	16 Nov 2011 08:21:01 -0000
+@@ -334,16 +334,29 @@ SSL_IMPORT SECStatus SSL_SecurityStatus(
+ **	"fd" the socket "file" descriptor
+ */
+ SSL_IMPORT CERTCertificate *SSL_PeerCertificate(PRFileDesc *fd);
+ 
+ /*
+ ** Authenticate certificate hook. Called when a certificate comes in
+ ** (because of SSL_REQUIRE_CERTIFICATE in SSL_Enable) to authenticate the
+ ** certificate.
++**
++** The authenticate certificate hook must return SECSuccess to indicate the
++** certificate is valid, SECFailure to indicate the certificate is invalid,
++** or SECWouldBlock if the application will authenticate the certificate
++** asynchronously.
++**
++** If the authenticate certificate hook returns SECFailure, then the bad cert
++** hook will be called. The bad cert handler is NEVER called if the
++** authenticate certificate hook returns SECWouldBlock.
++** 
++** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
++** information about the asynchronous behavior that occurs when the
++** authenticate certificate hook returns SECWouldBlock.
+ */
+ typedef SECStatus (PR_CALLBACK *SSLAuthCertificate)(void *arg, PRFileDesc *fd, 
+                                                     PRBool checkSig,
+                                                     PRBool isServer);
+ 
+ SSL_IMPORT SECStatus SSL_AuthCertificateHook(PRFileDesc *fd, 
+ 					     SSLAuthCertificate f,
+ 				             void *arg);
+@@ -437,16 +450,25 @@ SSL_IMPORT PRFileDesc *SSL_ReconfigFD(PR
+  *	a - pkcs11 application specific data
+  */
+ SSL_IMPORT SECStatus SSL_SetPKCS11PinArg(PRFileDesc *fd, void *a);
+ 
+ /*
+ ** This is a callback for dealing with server certs that are not authenticated
+ ** by the client.  The client app can decide that it actually likes the
+ ** cert by some external means and restart the connection.
++**
++** The bad cert hook must return SECSuccess to override the result of the
++** authenticate certificate hook, SECFailure if the certificate should still be
++** considered invalid, or SECWouldBlock if the application will authenticate
++** the certificate asynchronously.
++**
++** See the documentation for SSL_RestartHandshakeAfterAuthCertificate for more
++** information about the asynchronous behavior that occurs when the bad cert
++** hook returns SECWouldBlock.
+ */
+ typedef SECStatus (PR_CALLBACK *SSLBadCertHandler)(void *arg, PRFileDesc *fd);
+ SSL_IMPORT SECStatus SSL_BadCertHook(PRFileDesc *fd, SSLBadCertHandler f, 
+ 				     void *arg);
+ 
+ /*
+ ** Configure SSL socket for running a secure server. Needs the
+ ** certificate for the server and the servers private key. The arguments
+@@ -735,11 +757,58 @@ SSL_IMPORT SECStatus SSL_HandshakeNegoti
+  */
+ extern PRBool NSSSSL_VersionCheck(const char *importedVersion);
+ 
+ /*
+  * Returns a const string of the SSL library version.
+  */
+ extern const char *NSSSSL_GetVersion(void);
+ 
++/* Restart an SSL connection that was paused to do asynchronous certificate
++ * chain validation (when the auth certificate hook or bad cert handler
++ * returned SECWouldBlock).
++ *
++ * Currently, this function works only for the client role of a connection; it
++ * does not work for the server role.
++ *
++ * The application MUST call SSL_RestartHandshakeAfterAuthCertificate after it
++ * has successfully validated the peer's certificate to continue the SSL
++ * handshake.
++ *
++ * The application MUST NOT call SSL_RestartHandshakeAfterAuthCertificate when
++ * certificate validation fails; instead, it should just close the connection.
++ *
++ * This function will not complete the entire handshake. The application must
++ * call SSL_ForceHandshake, PR_Recv, PR_Send, etc. after calling this function
++ * to force the handshake to complete.
++ *
++ * libssl will wait for the peer's certificate to be authenticated before
++ * calling the handshake callback, sending a client certificate,
++ * sending any application data, or returning any application data to the
++ * application (on the first handshake on a connection only).
++ *
++ * However, libssl may send and receive handshake messages while waiting for
++ * the application to call SSL_RestartHandshakeAfterAuthCertificate, and it may
++ * call other callbacks (e.g, the client auth data hook) before
++ * SSL_RestartHandshakeAfterAuthCertificate has been called. 
++ *
++ * An application that uses this asynchronous mechanism will usually have lower
++ * handshake latency if it has to do public key operations on the certificate
++ * chain during the authentication, especially if it does so in parallel on
++ * another thread. However, if the application can authenticate the peer's
++ * certificate quickly then it may be more efficient to use the synchronous
++ * mechanism (i.e. returning SECFailure/SECSuccess instead of SECWouldBlock
++ * from the authenticate certificate hook).
++ *
++ * Be careful about converting an application from synchronous cert validation
++ * to asynchronous certificate validation. A naive conversion is likely to
++ * result in deadlocks; e.g. the application will wait in PR_Poll for network
++ * I/O on the connection while all network I/O on the connection is blocked
++ * waiting for this function to be called.
++ *
++ * Returns SECFailure on failure, SECSuccess on success. Never returns
++ * SECWouldBlock.
++ */
++SSL_IMPORT SECStatus SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd);
++
+ SEC_END_PROTOS
+ 
+ #endif /* __ssl_h_ */
+Index: mozilla/security/nss/lib/ssl/ssl3con.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3con.c,v
+retrieving revision 1.155
+diff -u -8 -p -r1.155 ssl3con.c
+--- mozilla/security/nss/lib/ssl/ssl3con.c	11 Nov 2011 19:06:52 -0000	1.155
++++ mozilla/security/nss/lib/ssl/ssl3con.c	16 Nov 2011 08:21:02 -0000
+@@ -5644,153 +5644,161 @@ loser:
+     PORT_SetError(errCode);
+     rv = SECFailure;
+ done:
+     if (arena != NULL)
+     	PORT_FreeArena(arena, PR_FALSE);
+     return rv;
+ }
+ 
+-/*
+- * attempt to restart the handshake after asynchronously handling
+- * a request for the client's certificate.
+- *
+- * inputs:
+- *	cert	Client cert chosen by application.
+- *		Note: ssl takes this reference, and does not bump the
+- *		reference count.  The caller should drop its reference
+- *		without calling CERT_DestroyCert after calling this function.
+- *
+- *	key	Private key associated with cert.  This function makes a
+- *		copy of the private key, so the caller remains responsible
+- *		for destroying its copy after this function returns.
+- *
+- *	certChain  DER-encoded certs, client cert and its signers.
+- *		Note: ssl takes this reference, and does not copy the chain.
+- *		The caller should drop its reference without destroying the
+- *		chain.  SSL will free the chain when it is done with it.
+- *
+- * Return value: XXX
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Caller holds 1stHandshakeLock.
+- */
+-SECStatus
+-ssl3_RestartHandshakeAfterCertReq(sslSocket *         ss,
+-				CERTCertificate *    cert,
+-				SECKEYPrivateKey *   key,
+-				CERTCertificateList *certChain)
+-{
+-    SECStatus        rv          = SECSuccess;
+-
+-    if (MSB(ss->version) == MSB(SSL_LIBRARY_VERSION_3_0)) {
+-	/* XXX This code only works on the initial handshake on a connection,
+-	** XXX It does not work on a subsequent handshake (redo).
+-	*/
+-	if (ss->handshake != 0) {
+-	    ss->handshake               = ssl_GatherRecord1stHandshake;
+-	    ss->ssl3.clientCertificate = cert;
+-	    ss->ssl3.clientCertChain   = certChain;
+-	    if (key == NULL) {
+-		(void)SSL3_SendAlert(ss, alert_warning, no_certificate);
+-		ss->ssl3.clientPrivateKey = NULL;
+-	    } else {
+-		ss->ssl3.clientPrivateKey = SECKEY_CopyPrivateKey(key);
+-	    }
+-	    ssl_GetRecvBufLock(ss);
+-	    if (ss->ssl3.hs.msgState.buf != NULL) {
+-		rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+-	    }
+-	    ssl_ReleaseRecvBufLock(ss);
+-	}
+-    }
+-    return rv;
+-}
+-
+ PRBool
+ ssl3_CanFalseStart(sslSocket *ss) {
+     PRBool rv;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ 
++    /* XXX: does not take into account whether we are waiting for
++     * SSL_RestartHandshakeAfterAuthCertificate or
++     * SSL_RestartHandshakeAfterCertReq. If/when that is done, this function
++     * could return different results each time it would be called.
++     */
++
+     ssl_GetSpecReadLock(ss);
+     rv = ss->opt.enableFalseStart &&
+ 	 !ss->sec.isServer &&
+ 	 !ss->ssl3.hs.isResuming &&
+ 	 ss->ssl3.cwSpec &&
+ 	 ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
+ 	(ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
+ 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
+ 	 ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
+     ssl_ReleaseSpecReadLock(ss);
+     return rv;
+ }
+ 
++static SECStatus ssl3_SendClientSecondRound(sslSocket *ss);
++
+ /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
+  * ssl3 Server Hello Done message.
+  * Caller must hold Handshake and RecvBuf locks.
+  */
+ static SECStatus
+ ssl3_HandleServerHelloDone(sslSocket *ss)
+ {
+     SECStatus     rv;
+     SSL3WaitState ws          = ss->ssl3.hs.ws;
+-    PRBool        send_verify = PR_FALSE;
+ 
+     SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello_done handshake",
+ 		SSL_GETPID(), ss->fd));
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
+ 
+     if (ws != wait_hello_done  &&
+         ws != wait_server_cert &&
+ 	ws != wait_server_key  &&
+ 	ws != wait_cert_request) {
+ 	SSL3_SendAlert(ss, alert_fatal, unexpected_message);
+ 	PORT_SetError(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE);
+ 	return SECFailure;
+     }
+ 
++    rv = ssl3_SendClientSecondRound(ss);
++
++    return rv;
++}
++
++/* Called from ssl3_HandleServerHelloDone and
++ * ssl3_RestartHandshakeAfterServerCert.
++ *
++ * Caller must hold Handshake and RecvBuf locks.
++ */
++static SECStatus
++ssl3_SendClientSecondRound(sslSocket *ss)
++{
++    SECStatus rv;
++    PRBool sendClientCert;
++
++    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
++    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
++
++    sendClientCert = !ss->ssl3.sendEmptyCert &&
++		     ss->ssl3.clientCertChain  != NULL &&
++		     ss->ssl3.clientPrivateKey != NULL;
++
++    /* We must wait for the server's certificate to be authenticated before
++     * sending the client certificate in order to disclosing the client
++     * certificate to an attacker that does not have a valid cert for the
++     * domain we are connecting to.
++     *
++     * XXX: We should do the same for the NPN extension, but for that we
++     * need an option to give the application the ability to leak the NPN
++     * information to get better performance.
++     *
++     * During the initial handshake on a connection, we never send/receive
++     * application data until we have authenticated the server's certificate;
++     * i.e. we have fully authenticated the handshake before using the cipher
++     * specs agreed upon for that handshake. During a renegotiation, we may
++     * continue sending and receiving application data during the handshake
++     * interleaved with the handshake records. If we were to send the client's
++     * second round for a renegotiation before the server's certificate was
++     * authenticated, then the application data sent/received after this point
++     * would be using cipher spec that hadn't been authenticated. By waiting
++     * until the server's certificate has been authenticated during 
++     * renegotiations, we ensure that renegotiations have the same property
++     * as initial handshakes; i.e. we have fully authenticated the handshake
++     * before using the cipher specs agreed upon for that handshake for
++     * application data.
++     */
++    if (ss->ssl3.hs.restartTarget) {
++        PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget");
++        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++        return SECFailure;
++    }
++    if (ss->ssl3.hs.authCertificatePending &&
++        (sendClientCert || ss->ssl3.sendEmptyCert || ss->firstHsDone)) {
++        ss->ssl3.hs.restartTarget = ssl3_SendClientSecondRound;
++        return SECWouldBlock;
++    }
++
+     ssl_GetXmitBufLock(ss);		/*******************************/
+ 
+     if (ss->ssl3.sendEmptyCert) {
+ 	ss->ssl3.sendEmptyCert = PR_FALSE;
+ 	rv = ssl3_SendEmptyCertificate(ss);
+ 	/* Don't send verify */
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* error code is set. */
+     	}
+-    } else
+-    if (ss->ssl3.clientCertChain  != NULL &&
+-	ss->ssl3.clientPrivateKey != NULL) {
+-	send_verify = PR_TRUE;
++    } else if (sendClientCert) {
+ 	rv = ssl3_SendCertificate(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* error code is set. */
+     	}
+     }
+ 
+     rv = ssl3_SendClientKeyExchange(ss);
+     if (rv != SECSuccess) {
+     	goto loser;	/* err is set. */
+     }
+ 
+-    if (send_verify) {
++    if (sendClientCert) {
+ 	rv = ssl3_SendCertificateVerify(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* err is set. */
+         }
+     }
++
+     rv = ssl3_SendChangeCipherSpecs(ss);
+     if (rv != SECSuccess) {
+ 	goto loser;	/* err code was set. */
+     }
+ 
++    /* XXX: If the server's certificate hasn't been authenticated by this
++     * point, then we may be leaking this NPN message to an attacker.
++     */
+     if (!ss->firstHsDone) {
+ 	rv = ssl3_SendNextProto(ss);
+ 	if (rv != SECSuccess) {
+ 	    goto loser;	/* err code was set. */
+ 	}
+     }
+ 
+     rv = ssl3_SendFinished(ss, 0);
+@@ -7809,18 +7817,16 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
+  * ssl3 Certificate message.
+  * Caller must hold Handshake and RecvBuf locks.
+  */
+ static SECStatus
+ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ {
+     ssl3CertNode *   c;
+     ssl3CertNode *   lastCert 	= NULL;
+-    ssl3CertNode *   certs 	= NULL;
+-    PRArenaPool *    arena 	= NULL;
+     PRInt32          remaining  = 0;
+     PRInt32          size;
+     SECStatus        rv;
+     PRBool           isServer	= (PRBool)(!!ss->sec.isServer);
+     PRBool           trusted 	= PR_FALSE;
+     PRBool           isTLS;
+     SSL3AlertDescription desc	= bad_certificate;
+     int              errCode    = SSL_ERROR_RX_MALFORMED_CERTIFICATE;
+@@ -7867,21 +7873,21 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 	    goto alert_loser;
+     	/* This is TLS's version of a no_certificate alert. */
+     	/* I'm a server. I've requested a client cert. He hasn't got one. */
+ 	rv = ssl3_HandleNoCertificate(ss);
+ 	if (rv != SECSuccess) {
+ 	    errCode = PORT_GetError();
+ 	    goto loser;
+ 	}
+-	goto cert_block;
++	goto server_no_cert;
+     }
+ 
+-    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+-    if ( arena == NULL ) {
++    ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
++    if (ss->ssl3.peerCertArena == NULL) {
+ 	goto loser;	/* don't send alerts on memory errors */
+     }
+ 
+     /* First get the peer cert. */
+     remaining -= 3;
+     if (remaining < 0)
+ 	goto decode_loser;
+ 
+@@ -7921,17 +7927,17 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 	    goto decode_loser;
+ 
+ 	certItem.data = b;
+ 	certItem.len = size;
+ 	b      += size;
+ 	length -= size;
+ 	remaining -= size;
+ 
+-	c = PORT_ArenaNew(arena, ssl3CertNode);
++	c = PORT_ArenaNew(ss->ssl3.peerCertArena, ssl3CertNode);
+ 	if (c == NULL) {
+ 	    goto loser;	/* don't send alerts on memory errors */
+ 	}
+ 
+ 	c->cert = CERT_NewTempCertificate(ss->dbHandle, &certItem, NULL,
+ 	                                  PR_FALSE, PR_TRUE);
+ 	if (c->cert == NULL) {
+ 	    goto ambiguous_err;
+@@ -7939,51 +7945,55 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 
+ 	if (c->cert->trust)
+ 	    trusted = PR_TRUE;
+ 
+ 	c->next = NULL;
+ 	if (lastCert) {
+ 	    lastCert->next = c;
+ 	} else {
+-	    certs = c;
++	    ss->ssl3.peerCertChain = c;
+ 	}
+ 	lastCert = c;
+     }
+ 
+     if (remaining != 0)
+         goto decode_loser;
+ 
+     SECKEY_UpdateCertPQG(ss->sec.peerCert);
+ 
++    ss->ssl3.hs.authCertificatePending = PR_FALSE;
++
+     /*
+      * Ask caller-supplied callback function to validate cert chain.
+      */
+     rv = (SECStatus)(*ss->authCertificate)(ss->authCertificateArg, ss->fd,
+ 					   PR_TRUE, isServer);
+     if (rv) {
+ 	errCode = PORT_GetError();
+-	if (!ss->handleBadCert) {
+-	    goto bad_cert;
++	if (rv != SECWouldBlock) {
++	    if (ss->handleBadCert) {
++		rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd);
++	    }
+ 	}
+-	rv = (SECStatus)(*ss->handleBadCert)(ss->badCertArg, ss->fd);
+-	if ( rv ) {
+-	    if ( rv == SECWouldBlock ) {
+-		/* someone will handle this connection asynchronously*/
+-		SSL_DBG(("%d: SSL3[%d]: go to async cert handler",
+-			 SSL_GETPID(), ss->fd));
+-		ss->ssl3.peerCertChain = certs;
+-		certs               = NULL;
+-		ssl3_SetAlwaysBlock(ss);
+-		goto cert_block;
++
++	if (rv == SECWouldBlock) {
++	    if (ss->sec.isServer) {
++		errCode = SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS;
++		rv = SECFailure;
++		goto loser;
+ 	    }
+-	    /* cert is bad */
++
++            ss->ssl3.hs.authCertificatePending = PR_TRUE;
++            rv = SECSuccess;
++	}
++        
++        if (rv != SECSuccess) {
+ 	    goto bad_cert;
+ 	}
+-	/* cert is good */
+     }
+ 
+     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
+ 
+     if (!ss->sec.isServer) {
+         CERTCertificate *cert = ss->sec.peerCert;
+ 
+ 	/* set the server authentication and key exchange types and sizes
+@@ -8021,39 +8031,38 @@ ssl3_HandleCertificate(sslSocket *ss, SS
+ 		     * destroy pubKey and goto bad_cert
+ 		     */
+ 		}
+ 	    }
+ #endif /* NSS_ENABLE_ECC */
+ 	    SECKEY_DestroyPublicKey(pubKey); 
+ 	    pubKey = NULL;
+     	}
+-    }
+ 
+-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
+-
+-cert_block:
+-    if (ss->sec.isServer) {
+-	ss->ssl3.hs.ws = wait_client_key;
+-    } else {
+ 	ss->ssl3.hs.ws = wait_cert_request; /* disallow server_key_exchange */
+ 	if (ss->ssl3.hs.kea_def->is_limited ||
+ 	    /* XXX OR server cert is signing only. */
+ #ifdef NSS_ENABLE_ECC
+ 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
+ 	    ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa ||
+ #endif /* NSS_ENABLE_ECC */
+ 	    ss->ssl3.hs.kea_def->exchKeyType == kt_dh) {
+ 	    ss->ssl3.hs.ws = wait_server_key; /* allow server_key_exchange */
+ 	}
++    } else {
++server_no_cert:
++	ss->ssl3.hs.ws = wait_client_key;
+     }
+ 
+-    /* rv must normally be equal to SECSuccess here.  If we called
+-     * handleBadCert, it can also be SECWouldBlock.
+-     */
++    PORT_Assert(rv == SECSuccess);
++    if (rv != SECSuccess) {
++	errCode = SEC_ERROR_LIBRARY_FAILURE;
++	rv = SECFailure;
++	goto loser;
++    }
+     return rv;
+ 
+ ambiguous_err:
+     errCode = PORT_GetError();
+     switch (errCode) {
+     case PR_OUT_OF_MEMORY_ERROR:
+     case SEC_ERROR_BAD_DATABASE:
+     case SEC_ERROR_NO_MEMORY:
+@@ -8094,64 +8103,69 @@ bad_cert:	/* caller has set errCode. */
+ 
+ decode_loser:
+     desc = isTLS ? decode_error : bad_certificate;
+ 
+ alert_loser:
+     (void)SSL3_SendAlert(ss, alert_fatal, desc);
+ 
+ loser:
+-    ss->ssl3.peerCertChain = certs;  certs = NULL;  arena = NULL;
+     ssl3_CleanupPeerCerts(ss);
+ 
+     if (ss->sec.peerCert != NULL) {
+ 	CERT_DestroyCertificate(ss->sec.peerCert);
+ 	ss->sec.peerCert = NULL;
+     }
+     (void)ssl_MapLowLevelError(errCode);
+     return SECFailure;
+ }
+ 
++static SECStatus ssl3_FinishHandshake(sslSocket *ss);
+ 
+-/* restart an SSL connection that we stopped to run certificate dialogs
+-** XXX	Need to document here how an application marks a cert to show that
+-**	the application has accepted it (overridden CERT_VerifyCert).
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Return value: XXX
+- *
+- * Caller holds 1stHandshakeLock.
++/* Caller must hold 1stHandshakeLock.
+ */
+-int
+-ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
++SECStatus
++ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss)
+ {
+-    int rv = SECSuccess;
++    SECStatus rv;
+ 
+-    if (MSB(ss->version) != MSB(SSL_LIBRARY_VERSION_3_0)) {
+-	SET_ERROR_CODE
+-    	return SECFailure;
+-    }
+-    if (!ss->ssl3.initialized) {
+-	SET_ERROR_CODE
+-    	return SECFailure;
++    PORT_Assert(ss->opt.noLocks || ssl_Have1stHandshakeLock(ss));
++
++    if (ss->sec.isServer) {
++	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS);
++	return SECFailure;
+     }
+ 
+-    if (ss->handshake != NULL) {
+-	ss->handshake = ssl_GatherRecord1stHandshake;
+-	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
++    ssl_GetRecvBufLock(ss);
++    ssl_GetSSL3HandshakeLock(ss);
+ 
+-	ssl_GetRecvBufLock(ss);
+-	if (ss->ssl3.hs.msgState.buf != NULL) {
+-	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
+-	}
+-	ssl_ReleaseRecvBufLock(ss);
++    if (!ss->ssl3.hs.authCertificatePending) {
++        PORT_SetError(PR_INVALID_STATE_ERROR);
++        rv = SECFailure;
++    } else {
++        ss->ssl3.hs.authCertificatePending = PR_FALSE;
++        if (ss->ssl3.hs.restartTarget != NULL) {
++            sslRestartTarget target = ss->ssl3.hs.restartTarget;
++            ss->ssl3.hs.restartTarget = NULL;
++            rv = target(ss);
++	    /* Even if we blocked here, we have accomplished enough to claim
++	      * success. Any remaining work will be taken care of by subsequent
++              * calls to SSL_ForceHandshake/PR_Send/PR_Read/etc. 
++	      */
++            if (rv == SECWouldBlock) {
++                rv = SECSuccess;
++            }
++        } else {
++            rv = SECSuccess;
++        }
+     }
+ 
++    ssl_ReleaseSSL3HandshakeLock(ss);
++    ssl_ReleaseRecvBufLock(ss);
++
+     return rv;
+ }
+ 
+ static SECStatus
+ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
+ 			PRBool          isServer,
+                 const   SSL3Finished *  hashes,
+                         TLSFinished  *  tlsFinished)
+@@ -8494,19 +8508,16 @@ ssl3_HandleFinished(sslSocket *ss, SSL3O
+     }
+ 
+ xmit_loser:
+     ssl_ReleaseXmitBufLock(ss);	/*************************************/
+     if (rv != SECSuccess) {
+         return rv;
+     }
+ 
+-    /* The first handshake is now completed. */
+-    ss->handshake           = NULL;
+-    ss->firstHsDone         = PR_TRUE;
+     ss->gs.writeOffset = 0;
+     ss->gs.readOffset  = 0;
+ 
+     if (ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa) {
+ 	effectiveExchKeyType = kt_rsa;
+     } else {
+ 	effectiveExchKeyType = ss->ssl3.hs.kea_def->exchKeyType;
+     }
+@@ -8546,20 +8557,52 @@ xmit_loser:
+ 					       effectiveExchKeyType);
+ 	    sid->u.ssl3.keys.msIsWrapped = PR_TRUE;
+ 	}
+ 	ssl_ReleaseSpecReadLock(ss);  /*************************************/
+ 
+ 	/* If the wrap failed, we don't cache the sid.
+ 	 * The connection continues normally however.
+ 	 */
+-	if (rv == SECSuccess) {
+-	    (*ss->sec.cache)(sid);
+-	}
++	ss->ssl3.hs.cacheSID = rv == SECSuccess;
+     }
++
++    if (ss->ssl3.hs.authCertificatePending) {
++      if (ss->ssl3.hs.restartTarget) {
++          PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
++          PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
++          return SECFailure;
++      }
++
++      ss->ssl3.hs.restartTarget = ssl3_FinishHandshake;
++      return SECWouldBlock;
++    }
++    
++    rv = ssl3_FinishHandshake(ss);
++    return rv;
++}
++
++SECStatus
++ssl3_FinishHandshake(sslSocket * ss)
++{
++    SECStatus rv;
++    
++    PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
++    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
++    PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
++
++    /* The first handshake is now completed. */
++    ss->handshake           = NULL;
++    ss->firstHsDone         = PR_TRUE;
++
++    if (ss->sec.ci.sid->cached == never_cached &&
++	!ss->opt.noCache && ss->sec.cache && ss->ssl3.hs.cacheSID) {
++	(*ss->sec.cache)(ss->sec.ci.sid);
++    }
++
+     ss->ssl3.hs.ws = idle_handshake;
+ 
+     /* Do the handshake callback for sslv3 here, if we cannot false start. */
+     if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
+ 	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+     }
+ 
+     return SECSuccess;
+Index: mozilla/security/nss/lib/ssl/ssl3gthr.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3gthr.c,v
+retrieving revision 1.10
+diff -u -8 -p -r1.10 ssl3gthr.c
+--- mozilla/security/nss/lib/ssl/ssl3gthr.c	30 Jul 2010 03:00:17 -0000	1.10
++++ mozilla/security/nss/lib/ssl/ssl3gthr.c	16 Nov 2011 08:21:02 -0000
+@@ -187,31 +187,63 @@ int
+ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
+ {
+     SSL3Ciphertext cText;
+     int            rv;
+     PRBool         canFalseStart = PR_FALSE;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     do {
+-	/* bring in the next sslv3 record. */
+-	rv = ssl3_GatherData(ss, &ss->gs, flags);
+-	if (rv <= 0) {
+-	    return rv;
+-	}
+-	
+-	/* decipher it, and handle it if it's a handshake. 
+-	 * If it's application data, ss->gs.buf will not be empty upon return. 
+-	 * If it's a change cipher spec, alert, or handshake message,
+-	 * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
+-	 */
+-	cText.type    = (SSL3ContentType)ss->gs.hdr[0];
+-	cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
+-	cText.buf     = &ss->gs.inbuf;
+-	rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
++        /* Without this, we may end up wrongly reporting
++         * SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the
++         * peer while we are waiting to be restarted. 
++         */
++        ssl_GetSSL3HandshakeLock(ss);
++        rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure;
++        ssl_ReleaseSSL3HandshakeLock(ss);
++        if (rv != SECSuccess) {
++            PORT_SetError(PR_WOULD_BLOCK_ERROR);
++            return (int) SECFailure;
++        }
++
++        /* Treat an empty msgState like a NULL msgState. (Most of the time
++         * when ssl3_HandleHandshake returns SECWouldBlock, it leaves
++         * behind a non-NULL but zero-length msgState).
++         * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record
++         */
++        if (ss->ssl3.hs.msgState.buf != NULL) {
++            if (ss->ssl3.hs.msgState.len == 0) {
++                ss->ssl3.hs.msgState.buf = NULL;
++            }
++        }
++
++        if (ss->ssl3.hs.msgState.buf != NULL) {
++            /* ssl3_HandleHandshake previously returned SECWouldBlock and the
++             * as-yet-unprocessed plaintext of that previous handshake record.
++             * We need to process it now before we overwrite it with the next
++             * handshake record.
++             */
++	    rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf);
++        } else {
++	    /* bring in the next sslv3 record. */
++	    rv = ssl3_GatherData(ss, &ss->gs, flags);
++	    if (rv <= 0) {
++	        return rv;
++	    }
++
++	    /* decipher it, and handle it if it's a handshake. 
++	     * If it's application data, ss->gs.buf will not be empty upon return. 
++	     * If it's a change cipher spec, alert, or handshake message,
++	     * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess.
++	     */
++	    cText.type    = (SSL3ContentType)ss->gs.hdr[0];
++	    cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2];
++	    cText.buf     = &ss->gs.inbuf;
++	    rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf);
++        }
+ 	if (rv < 0) {
+ 	    return ss->recvdCloseNotify ? 0 : rv;
+ 	}
+ 
+ 	/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
+ 	 * out of this loop early without finishing the handshake.
+ 	 */
+ 	if (ss->opt.enableFalseStart) {
+Index: mozilla/security/nss/lib/ssl/sslerr.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslerr.h,v
+retrieving revision 1.16
+diff -u -8 -p -r1.16 sslerr.h
+--- mozilla/security/nss/lib/ssl/sslerr.h	11 Nov 2011 19:06:52 -0000	1.16
++++ mozilla/security/nss/lib/ssl/sslerr.h	16 Nov 2011 08:21:02 -0000
+@@ -203,14 +203,16 @@ SSL_ERROR_UNSAFE_NEGOTIATION            
+ 
+ SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD	= (SSL_ERROR_BASE + 114),
+ 
+ SSL_ERROR_WEAK_SERVER_EPHEMERAL_DH_KEY  = (SSL_ERROR_BASE + 115),
+ 
+ SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID	= (SSL_ERROR_BASE + 116),
+ 
+ SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2 = (SSL_ERROR_BASE + 117),
++SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SERVERS = (SSL_ERROR_BASE + 118),
++SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_CLIENTS = (SSL_ERROR_BASE + 119),
+ 
+ SSL_ERROR_END_OF_LIST	/* let the c compiler determine the value of this. */
+ } SSLErrorCodes;
+ #endif /* NO_SECURITY_ERROR_ENUM */
+ 
+ #endif /* __SSL_ERR_H_ */
+Index: mozilla/security/nss/lib/ssl/sslimpl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslimpl.h,v
+retrieving revision 1.87
+diff -u -8 -p -r1.87 sslimpl.h
+--- mozilla/security/nss/lib/ssl/sslimpl.h	11 Nov 2011 19:06:52 -0000	1.87
++++ mozilla/security/nss/lib/ssl/sslimpl.h	16 Nov 2011 08:21:02 -0000
+@@ -745,16 +745,18 @@ struct TLSExtensionDataStr {
+     /* SNI Extension related data
+      * Names data is not coppied from the input buffer. It can not be
+      * used outside the scope where input buffer is defined and that
+      * is beyond ssl3_HandleClientHello function. */
+     SECItem *sniNameArr;
+     PRUint32 sniNameArrSize;
+ };
+ 
++typedef SECStatus (*sslRestartTarget)(sslSocket *);
++
+ /*
+ ** This is the "hs" member of the "ssl3" struct.
+ ** This entire struct is protected by ssl3HandshakeLock
+ */
+ typedef struct SSL3HandshakeStateStr {
+     SSL3Random            server_random;
+     SSL3Random            client_random;
+     SSL3WaitState         ws;
+@@ -784,16 +786,23 @@ const ssl3CipherSuiteDef *suite_def;
+     union {
+ 	TLSFinished       tFinished[2]; /* client, then server */
+ 	SSL3Hashes        sFinished[2];
+ 	SSL3Opaque        data[72];
+     }                     finishedMsgs;
+ #ifdef NSS_ENABLE_ECC
+     PRUint32              negotiatedECCurves; /* bit mask */
+ #endif /* NSS_ENABLE_ECC */
++
++    PRBool                authCertificatePending;
++    /* Which function should SSL_RestartHandshake* call if we're blocked?
++     * One of NULL, ssl3_SendClientSecondRound, or ssl3_FinishHandshake. */
++    sslRestartTarget      restartTarget;
++    /* Shared state between ssl3_HandleFinished and ssl3_FinishHandshake */
++    PRBool                cacheSID; 
+ } SSL3HandshakeState;
+ 
+ 
+ 
+ /*
+ ** This is the "ssl3" struct, as in "ss->ssl3".
+ ** note:
+ ** usually,   crSpec == cwSpec and prSpec == pwSpec.  
+@@ -1335,32 +1344,26 @@ extern SECStatus ssl3_KeyAndMacDeriveByp
+ 		    PRBool isTLS, PRBool isExport);
+ extern  SECStatus ssl3_MasterKeyDeriveBypass( ssl3CipherSpec * pwSpec,
+ 		    const unsigned char * cr, const unsigned char * sr,
+ 		    const SECItem * pms, PRBool isTLS, PRBool isRSA);
+ 
+ /* These functions are called from secnav, even though they're "private". */
+ 
+ extern int ssl2_SendErrorMessage(struct sslSocketStr *ss, int error);
+-extern int SSL_RestartHandshakeAfterServerCert(struct sslSocketStr *ss);
+ extern int SSL_RestartHandshakeAfterCertReq(struct sslSocketStr *ss,
+ 					    CERTCertificate *cert,
+ 					    SECKEYPrivateKey *key,
+ 					    CERTCertificateList *certChain);
+ extern sslSocket *ssl_FindSocket(PRFileDesc *fd);
+ extern void ssl_FreeSocket(struct sslSocketStr *ssl);
+ extern SECStatus SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level,
+ 				SSL3AlertDescription desc);
+ 
+-extern SECStatus ssl3_RestartHandshakeAfterCertReq(sslSocket *    ss,
+-					     CERTCertificate *    cert, 
+-					     SECKEYPrivateKey *   key,
+-					     CERTCertificateList *certChain);
+-
+-extern int ssl3_RestartHandshakeAfterServerCert(sslSocket *ss);
++extern SECStatus ssl3_RestartHandshakeAfterAuthCertificate(sslSocket *ss);
+ 
+ /*
+  * for dealing with SSL 3.0 clients sending SSL 2.0 format hellos
+  */
+ extern SECStatus ssl3_HandleV2ClientHello(
+     sslSocket *ss, unsigned char *buffer, int length);
+ extern SECStatus ssl3_StartHandshakeHash(
+     sslSocket *ss, unsigned char *buf, int length);
+Index: mozilla/security/nss/lib/ssl/sslsecur.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslsecur.c,v
+retrieving revision 1.51
+diff -u -8 -p -r1.51 sslsecur.c
+--- mozilla/security/nss/lib/ssl/sslsecur.c	11 Nov 2011 19:06:52 -0000	1.51
++++ mozilla/security/nss/lib/ssl/sslsecur.c	16 Nov 2011 08:21:02 -0000
+@@ -1458,86 +1458,66 @@ SSL_CertDBHandleSet(PRFileDesc *fd, CERT
+     if (!dbHandle) {
+     	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 	return SECFailure;
+     }
+     ss->dbHandle = dbHandle;
+     return SECSuccess;
+ }
+ 
+-/*
+- * attempt to restart the handshake after asynchronously handling
+- * a request for the client's certificate.
+- *
+- * inputs:  
+- *	cert	Client cert chosen by application.
+- *		Note: ssl takes this reference, and does not bump the 
+- *		reference count.  The caller should drop its reference
+- *		without calling CERT_DestroyCert after calling this function.
+- *
+- *	key	Private key associated with cert.  This function makes a 
+- *		copy of the private key, so the caller remains responsible 
+- *		for destroying its copy after this function returns.
+- *
+- *	certChain  Chain of signers for cert.  
+- *		Note: ssl takes this reference, and does not copy the chain.
+- *		The caller should drop its reference without destroying the 
+- *		chain.  SSL will free the chain when it is done with it.
+- *
+- * Return value: XXX
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
++/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
++ * this implementation exists to maintain link-time compatibility.
+  */
+ int
+ SSL_RestartHandshakeAfterCertReq(sslSocket *         ss,
+ 				CERTCertificate *    cert, 
+ 				SECKEYPrivateKey *   key,
+ 				CERTCertificateList *certChain)
+ {
+-    int              ret;
+-
+-    ssl_Get1stHandshakeLock(ss);   /************************************/
+-
+-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+-	ret = ssl3_RestartHandshakeAfterCertReq(ss, cert, key, certChain);
+-    } else {
+-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+-    	ret = SECFailure;
+-    }
+-
+-    ssl_Release1stHandshakeLock(ss);  /************************************/
+-    return ret;
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    return -1;
+ }
+ 
+-
+-/* restart an SSL connection that we stopped to run certificate dialogs 
+-** XXX	Need to document here how an application marks a cert to show that
+-**	the application has accepted it (overridden CERT_VerifyCert).
+- *
+- * XXX This code only works on the initial handshake on a connection, XXX
+- *     It does not work on a subsequent handshake (redo).
+- *
+- * Return value: XXX
+-*/
++/* DO NOT USE. This function was exported in ssl.def with the wrong signature;
++ * this implementation exists to maintain link-time compatibility.
++ */
+ int
+-SSL_RestartHandshakeAfterServerCert(sslSocket *ss)
++SSL_RestartHandshakeAfterServerCert(sslSocket * ss)
++{
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    return -1;
++}
++
++/* See documentation in ssl.h */
++SECStatus
++SSL_RestartHandshakeAfterAuthCertificate(PRFileDesc *fd)
+ {
+-    int rv	= SECSuccess;
++    SECStatus rv = SECSuccess;
++    sslSocket *ss = ssl_FindSocket(fd);
++
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_RestartHandshakeAfterPeerCert",
++		 SSL_GETPID(), fd));
++	return SECFailure;
++    }
+ 
+-    ssl_Get1stHandshakeLock(ss); 
++    ssl_Get1stHandshakeLock(ss);
+ 
+-    if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
+-	rv = ssl3_RestartHandshakeAfterServerCert(ss);
++    if (!ss->ssl3.initialized) {
++	PORT_SetError(SEC_ERROR_INVALID_ARGS);
++	rv = SECFailure;
++    } else if (ss->version < SSL_LIBRARY_VERSION_3_0) {
++	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
++	rv = SECFailure;
+     } else {
+-    	PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_SSL2);
+-    	rv = SECFailure;
++	rv = ssl3_RestartHandshakeAfterAuthCertificate(ss);
+     }
+ 
+     ssl_Release1stHandshakeLock(ss);
++
+     return rv;
+ }
+ 
+ /* For more info see ssl.h */
+ SECStatus 
+ SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
+                         void *arg)
+ {
new file mode 100644
--- /dev/null
+++ b/security/patches/bug-542832-ssl-restart-tstclnt-4.patch
@@ -0,0 +1,349 @@
+Index: mozilla/security/nss/cmd/tstclnt/tstclnt.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/cmd/tstclnt/tstclnt.c,v
+retrieving revision 1.64
+diff -u -8 -p -r1.64 tstclnt.c
+--- mozilla/security/nss/cmd/tstclnt/tstclnt.c	6 Oct 2011 22:42:33 -0000	1.64
++++ mozilla/security/nss/cmd/tstclnt/tstclnt.c	16 Nov 2011 08:24:12 -0000
+@@ -212,16 +212,18 @@ static void Usage(const char *progName)
+                     "-n nickname");
+     fprintf(stderr, 
+             "%-20s Bypass PKCS11 layer for SSL encryption and MACing.\n", "-B");
+     fprintf(stderr, "%-20s Disable SSL v2.\n", "-2");
+     fprintf(stderr, "%-20s Disable SSL v3.\n", "-3");
+     fprintf(stderr, "%-20s Disable TLS (SSL v3.1).\n", "-T");
+     fprintf(stderr, "%-20s Prints only payload data. Skips HTTP header.\n", "-S");
+     fprintf(stderr, "%-20s Client speaks first. \n", "-f");
++    fprintf(stderr, "%-20s Use synchronous certificate validation "
++                    "(required for SSL2)\n", "-O");
+     fprintf(stderr, "%-20s Override bad server cert. Make it OK.\n", "-o");
+     fprintf(stderr, "%-20s Disable SSL socket locking.\n", "-s");
+     fprintf(stderr, "%-20s Verbose progress reporting.\n", "-v");
+     fprintf(stderr, "%-20s Use export policy.\n", "-x");
+     fprintf(stderr, "%-20s Ping the server and then exit.\n", "-q");
+     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
+     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
+     fprintf(stderr, "%-20s Enable compression.\n", "-z");
+@@ -288,30 +290,54 @@ disableAllSSLCiphers(void)
+ 	    fprintf(stderr,
+ 	            "SSL_CipherPrefSet didn't like value 0x%04x (i = %d): %s\n",
+ 	    	   suite, i, SECU_Strerror(err));
+ 	    exit(2);
+ 	}
+     }
+ }
+ 
++typedef struct
++{
++   PRBool shouldPause; /* PR_TRUE if we should use asynchronous peer cert 
++                        * authentication */
++   PRBool isPaused;    /* PR_TRUE if libssl is waiting for us to validate the
++                        * peer's certificate and restart the handshake. */
++   void * dbHandle;    /* Certificate database handle to use while
++                        * authenticating the peer's certificate. */
++} ServerCertAuth;
++
+ /*
+  * Callback is called when incoming certificate is not valid.
+  * Returns SECSuccess to accept the cert anyway, SECFailure to reject.
+  */
+ static SECStatus 
+ ownBadCertHandler(void * arg, PRFileDesc * socket)
+ {
+     PRErrorCode err = PR_GetError();
+     /* can log invalid cert here */
+     fprintf(stderr, "Bad server certificate: %d, %s\n", err, 
+             SECU_Strerror(err));
+     return SECSuccess;	/* override, say it's OK. */
+ }
+ 
++static SECStatus 
++ownAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig,
++                       PRBool isServer)
++{
++    ServerCertAuth * serverCertAuth = (ServerCertAuth *) arg;
++
++    FPRINTF(stderr, "using asynchronous certificate validation\n", progName);
++
++    PORT_Assert(serverCertAuth->shouldPause);
++    PORT_Assert(!serverCertAuth->isPaused);
++    serverCertAuth->isPaused = PR_TRUE;
++    return SECWouldBlock;
++}
++
+ SECStatus
+ own_GetClientAuthData(void *                       arg,
+                       PRFileDesc *                 socket,
+                       struct CERTDistNamesStr *    caNames,
+                       struct CERTCertificateStr ** pRetCert,
+                       struct SECKEYPrivateKeyStr **pRetKey)
+ {
+     if (verbose > 1) {
+@@ -493,21 +519,47 @@ separateReqHeader(const PRFileDesc* outF
+     } else if (((c) >= 'a') && ((c) <= 'f')) { \
+ 	i = (c) - 'a' + 10; \
+     } else if (((c) >= 'A') && ((c) <= 'F')) { \
+ 	i = (c) - 'A' + 10; \
+     } else { \
+ 	Usage(progName); \
+     }
+ 
++static SECStatus
++restartHandshakeAfterServerCertIfNeeded(PRFileDesc * fd,
++                                        ServerCertAuth * serverCertAuth,
++                                        PRBool override)
++{
++    SECStatus rv;
++    
++    if (!serverCertAuth->isPaused)
++	return SECSuccess;
++    
++    FPRINTF(stderr, "%s: handshake was paused by auth certificate hook\n",
++            progName);
++
++    serverCertAuth->isPaused = PR_FALSE;
++    rv = SSL_AuthCertificate(serverCertAuth->dbHandle, fd, PR_TRUE, PR_FALSE);
++    if (rv != SECSuccess && override) {
++        rv = ownBadCertHandler(NULL, fd);
++    }
++    if (rv != SECSuccess) {
++	return rv;
++    }
++    
++    rv = SSL_RestartHandshakeAfterAuthCertificate(fd);
++
++    return rv;
++}
++    
+ int main(int argc, char **argv)
+ {
+     PRFileDesc *       s;
+     PRFileDesc *       std_out;
+-    CERTCertDBHandle * handle;
+     char *             host	=  NULL;
+     char *             certDir  =  NULL;
+     char *             nickname =  NULL;
+     char *             cipherString = NULL;
+     char *             tmp;
+     int                multiplier = 0;
+     SECStatus          rv;
+     PRStatus           status;
+@@ -525,51 +577,58 @@ int main(int argc, char **argv)
+     int                enableFalseStart = 0;
+     PRSocketOptionData opt;
+     PRNetAddr          addr;
+     PRPollDesc         pollset[2];
+     PRBool             pingServerFirst = PR_FALSE;
+     PRBool             clientSpeaksFirst = PR_FALSE;
+     PRBool             wrStarted = PR_FALSE;
+     PRBool             skipProtoHeader = PR_FALSE;
++    ServerCertAuth     serverCertAuth;
+     int                headerSeparatorPtrnId = 0;
+     int                error = 0;
+     PRUint16           portno = 443;
+     char *             hs1SniHostName = NULL;
+     char *             hs2SniHostName = NULL;
+     PLOptState *optstate;
+     PLOptStatus optstatus;
+     PRStatus prStatus;
+ 
++    serverCertAuth.shouldPause = PR_TRUE;
++    serverCertAuth.isPaused = PR_FALSE;
++    serverCertAuth.dbHandle = NULL;
++
+     progName = strrchr(argv[0], '/');
+     if (!progName)
+ 	progName = strrchr(argv[0], '\\');
+     progName = progName ? progName+1 : argv[0];
+ 
+     tmp = PR_GetEnv("NSS_DEBUG_TIMEOUT");
+     if (tmp && tmp[0]) {
+        int sec = PORT_Atoi(tmp);
+        if (sec > 0) {
+            maxInterval = PR_SecondsToInterval(sec);
+        }
+     }
+ 
+     optstate = PL_CreateOptState(argc, argv,
+-                                 "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
++                                 "23BOSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
+     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ 	switch (optstate->option) {
+ 	  case '?':
+ 	  default : Usage(progName); 			break;
+ 
+           case '2': disableSSL2 = 1; 			break;
+ 
+           case '3': disableSSL3 = 1; 			break;
+ 
+           case 'B': bypassPKCS11 = 1; 			break;
+ 
++          case 'O': serverCertAuth.shouldPause = PR_FALSE; break;
++
+           case 'S': skipProtoHeader = PR_TRUE;                 break;
+ 
+           case 'T': disableTLS  = 1; 			break;
+ 
+           case 'a': if (!hs1SniHostName) {
+                         hs1SniHostName = PORT_Strdup(optstate->value);
+                     } else if (!hs2SniHostName) {
+                         hs2SniHostName =  PORT_Strdup(optstate->value);
+@@ -645,24 +704,18 @@ int main(int argc, char **argv)
+     } else {
+ 	char *certDirTmp = certDir;
+ 	certDir = SECU_ConfigDirectory(certDirTmp);
+ 	PORT_Free(certDirTmp);
+     }
+     rv = NSS_Init(certDir);
+     if (rv != SECSuccess) {
+ 	SECU_PrintError(progName, "unable to open cert database");
+-#if 0
+-    rv = CERT_OpenVolatileCertDB(handle);
+-	CERT_SetDefaultCertDB(handle);
+-#else
+ 	return 1;
+-#endif
+     }
+-    handle = CERT_GetDefaultCertDB();
+ 
+     /* set the policy bits true for all the cipher suites. */
+     if (useExportPolicy)
+ 	NSS_SetExportPolicy();
+     else
+ 	NSS_SetDomesticPolicy();
+ 
+     /* all the SSL2 and SSL3 cipher suites are enabled by default. */
+@@ -871,17 +924,23 @@ int main(int argc, char **argv)
+     rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
+     if (rv != SECSuccess) {
+ 	SECU_PrintError(progName, "error enabling false start");
+ 	return 1;
+     }
+ 
+     SSL_SetPKCS11PinArg(s, &pwdata);
+ 
+-    SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
++    serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
++
++    if (serverCertAuth.shouldPause) {
++	SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
++    } else {
++	SSL_AuthCertificateHook(s, SSL_AuthCertificate, serverCertAuth.dbHandle);
++    }
+     if (override) {
+ 	SSL_BadCertHook(s, ownBadCertHandler, NULL);
+     }
+     SSL_GetClientAuthDataHook(s, own_GetClientAuthData, (void *)nickname);
+     SSL_HandshakeCallback(s, handshakeCallback, hs2SniHostName);
+     if (hs1SniHostName) {
+         SSL_SetURL(s, hs1SniHostName);
+     } else {
+@@ -979,16 +1038,24 @@ int main(int argc, char **argv)
+     ** socket, read data from socket and write to stdout.
+     */
+     FPRINTF(stderr, "%s: ready...\n", progName);
+ 
+     while (pollset[SSOCK_FD].in_flags | pollset[STDIN_FD].in_flags) {
+ 	char buf[4000];	/* buffer for stdin */
+ 	int nb;		/* num bytes read from stdin. */
+ 
++	rv = restartHandshakeAfterServerCertIfNeeded(s, &serverCertAuth,
++						     override);
++	if (rv != SECSuccess) {
++	    error = 254; /* 254 (usually) means "handshake failed" */
++	    SECU_PrintError(progName, "authentication of server cert failed");
++	    goto done;
++	}
++	        
+ 	pollset[SSOCK_FD].out_flags = 0;
+ 	pollset[STDIN_FD].out_flags = 0;
+ 
+ 	FPRINTF(stderr, "%s: about to call PR_Poll !\n", progName);
+ 	filesReady = PR_Poll(pollset, npds, PR_INTERVAL_NO_TIMEOUT);
+ 	if (filesReady < 0) {
+ 	    SECU_PrintError(progName, "select failed");
+ 	    error = 1;
+@@ -1037,16 +1104,25 @@ int main(int argc, char **argv)
+ 			    goto done;
+ 			}
+ 			cc = 0;
+ 		    }
+ 		    bufp += cc;
+ 		    nb   -= cc;
+ 		    if (nb <= 0) 
+ 		    	break;
++
++		    rv = restartHandshakeAfterServerCertIfNeeded(s,
++				&serverCertAuth, override);
++		    if (rv != SECSuccess) {
++			error = 254; /* 254 (usually) means "handshake failed" */
++			SECU_PrintError(progName, "authentication of server cert failed");
++			goto done;
++		    }
++
+ 		    pollset[SSOCK_FD].in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ 		    pollset[SSOCK_FD].out_flags = 0;
+ 		    FPRINTF(stderr,
+ 		            "%s: about to call PR_Poll on writable socket !\n", 
+ 			    progName);
+ 		    cc = PR_Poll(pollset, 1, PR_INTERVAL_NO_TIMEOUT);
+ 		    FPRINTF(stderr,
+ 		            "%s: PR_Poll returned with writable socket !\n", 
+Index: mozilla/security/nss/tests/ssl/ssl.sh
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/tests/ssl/ssl.sh,v
+retrieving revision 1.106
+diff -u -8 -p -r1.106 ssl.sh
+--- mozilla/security/nss/tests/ssl/ssl.sh	29 Jan 2010 22:36:25 -0000	1.106
++++ mozilla/security/nss/tests/ssl/ssl.sh	16 Nov 2011 08:24:14 -0000
+@@ -303,16 +303,26 @@ ssl_cov()
+                
+   exec < ${SSLCOV}
+   while read ectype tls param testname
+   do
+       echo "${testname}" | grep "EXPORT" > /dev/null 
+       EXP=$?
+       echo "${testname}" | grep "SSL2" > /dev/null
+       SSL2=$?
++
++      if [ "${SSL2}" -eq 0 ] ; then
++          # We cannot use asynchronous cert verification with SSL2
++          SSL2_FLAGS=-O
++      else
++          # Do not enable SSL2 for non-SSL2-specific tests. SSL2 is disabled by
++          # default in libssl but it is enabled by default in tstclnt; we want
++          # to test the libssl default whenever possible.
++          SSL2_FLAGS=-2
++      fi
+       
+       if [ "$NORM_EXT" = "Extended Test" -a "${SSL2}" -eq 0 ] ; then
+           echo "$SCRIPTNAME: skipping  $testname for $NORM_EXT"
+       elif [ "$ectype" = "ECC" -a -z "$NSS_ENABLE_ECC" ] ; then
+           echo "$SCRIPTNAME: skipping  $testname (ECC only)"
+       elif [ "$SERVER_MODE" = "fips" -o "$CLIENT_MODE" = "fips" ] && [ "$SSL2" -eq 0 -o "$EXP" -eq 0 ] ; then
+           echo "$SCRIPTNAME: skipping  $testname (non-FIPS only)"
+       elif [ "`echo $ectype | cut -b 1`" != "#" ] ; then
+@@ -345,21 +355,21 @@ ssl_cov()
+               is_selfserv_alive
+             else
+               kill_selfserv
+               start_selfserv
+               mixed=0
+             fi
+           fi
+ 
+-          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} \\"
++          echo "tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} \\"
+           echo "        -f -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE}"
+ 
+           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+-          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${CLIENT_OPTIONS} -f \
++          ${PROFTOOL} ${BINDIR}/tstclnt -p ${PORT} -h ${HOSTADDR} -c ${param} ${TLS_FLAG} ${SSL2_FLAGS} ${CLIENT_OPTIONS} -f \
+                   -d ${P_R_CLIENTDIR} -v -w nss < ${REQUEST_FILE} \
+                   >${TMP}/$HOST.tmp.$$  2>&1
+           ret=$?
+           cat ${TMP}/$HOST.tmp.$$ 
+           rm ${TMP}/$HOST.tmp.$$ 2>/dev/null
+           html_msg $ret 0 "${testname}" \
+                    "produced a returncode of $ret, expected is 0"
+       fi
--- a/storage/src/mozStorageStatementJSHelper.cpp
+++ b/storage/src/mozStorageStatementJSHelper.cpp
@@ -61,19 +61,23 @@ namespace storage {
 static
 JSBool
 stepFunc(JSContext *aCtx,
          PRUint32,
          jsval *_vp)
 {
   nsCOMPtr<nsIXPConnect> xpc(Service::getXPConnect());
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-  nsresult rv = xpc->GetWrappedNativeOfJSObject(
-    aCtx, JS_THIS_OBJECT(aCtx, _vp), getter_AddRefs(wrapper)
-  );
+  JSObject *obj = JS_THIS_OBJECT(aCtx, _vp);
+  if (!obj) {
+    return JS_FALSE;
+  }
+
+  nsresult rv =
+    xpc->GetWrappedNativeOfJSObject(aCtx, obj, getter_AddRefs(wrapper));
   if (NS_FAILED(rv)) {
     ::JS_ReportError(aCtx, "mozIStorageStatement::step() could not obtain native statement");
     return JS_FALSE;
   }
 
 #ifdef DEBUG
   {
     nsCOMPtr<mozIStorageStatement> isStatement(
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -205,16 +205,20 @@ Tester.prototype = {
         try {
           func.apply(testScope);
         }
         catch (ex) {
           this.currentTest.addResult(new testResult(false, "Cleanup function threw an exception", ex, false));
         }
       };
 
+      if (this.SimpleTest.isExpectingUncaughtException()) {
+        this.currentTest.addResult(new testResult(false, "expectUncaughtException was called but no uncaught exception was detected!", "", false));
+      }
+
       // Clear document.popupNode.  The test could have set it to a custom value
       // for its own purposes, nulling it out it will go back to the default
       // behavior of returning the last opened popup.
       document.popupNode = null;
 
       // Note the test run time
       let time = Date.now() - this.lastStartTime;
       this.dumper.dump("INFO TEST-END | " + this.currentTest.path + " | finished in " + time + "ms\n");
@@ -245,26 +249,28 @@ Tester.prototype = {
       this.currentTestIndex++;
       this.execTest();
     }).bind(this));
   },
 
   execTest: function Tester_execTest() {
     this.dumper.dump("TEST-START | " + this.currentTest.path + "\n");
 
+    this.SimpleTest.reset();
+
     // Load the tests into a testscope
     this.currentTest.scope = new testScope(this, this.currentTest);
 
     // Import utils in the test scope.
     this.currentTest.scope.EventUtils = this.EventUtils;
     this.currentTest.scope.SimpleTest = this.SimpleTest;
     this.currentTest.scope.gTestPath = this.currentTest.path;
 
     // Override SimpleTest methods with ours.
-    ["ok", "is", "isnot", "todo", "todo_is", "todo_isnot"].forEach(function(m) {
+    ["ok", "is", "isnot", "todo", "todo_is", "todo_isnot", "info"].forEach(function(m) {
       this.SimpleTest[m] = this[m];
     }, this.currentTest.scope);
 
     //load the tools to work with chrome .jar and remote
     try {
       this._scriptLoader.loadSubScript("chrome://mochikit/content/chrome-harness.js", this.currentTest.scope);
     } catch (ex) { /* no chrome-harness tools */ }
 
@@ -291,17 +297,23 @@ Tester.prototype = {
         this.currentTest.scope.waitForExplicitFinish();
         var result = this.currentTest.scope.generatorTest();
         this.currentTest.scope.__generator = result;
         result.next();
       } else {
         this.currentTest.scope.test();
       }
     } catch (ex) {
-      this.currentTest.addResult(new testResult(false, "Exception thrown", ex, false));
+      var isExpected = !!this.SimpleTest.isExpectingUncaughtException();
+      if (!this.SimpleTest.isIgnoringAllUncaughtExceptions()) {
+        this.currentTest.addResult(new testResult(isExpected, "Exception thrown", ex, false));
+        this.SimpleTest.expectUncaughtException(false);
+      } else {
+        this.currentTest.addResult(new testMessage("Exception thrown: " + ex));
+      }
       this.currentTest.scope.finish();
     }
 
     // If the test ran synchronously, move to the next test, otherwise the test
     // will trigger the next test when it is done.
     if (this.currentTest.scope.__done) {
       this.nextTest();
     }
@@ -374,45 +386,44 @@ function testMessage(aName) {
   this.info = true;
   this.result = "TEST-INFO";
 }
 
 // Need to be careful adding properties to this object, since its properties
 // cannot conflict with global variables used in tests.
 function testScope(aTester, aTest) {
   this.__tester = aTester;
-  this.__browserTest = aTest;
 
   var self = this;
   this.ok = function test_ok(condition, name, diag, stack) {
-    self.__browserTest.addResult(new testResult(condition, name, diag, false,
-                                                stack ? stack : Components.stack.caller));
+    aTest.addResult(new testResult(condition, name, diag, false,
+                                   stack ? stack : Components.stack.caller));
   };
   this.is = function test_is(a, b, name) {
     self.ok(a == b, name, "Got " + a + ", expected " + b, false,
             Components.stack.caller);
   };
   this.isnot = function test_isnot(a, b, name) {
     self.ok(a != b, name, "Didn't expect " + a + ", but got it", false,
             Components.stack.caller);
   };
   this.todo = function test_todo(condition, name, diag, stack) {
-    self.__browserTest.addResult(new testResult(!condition, name, diag, true,
-                                                stack ? stack : Components.stack.caller));
+    aTest.addResult(new testResult(!condition, name, diag, true,
+                                   stack ? stack : Components.stack.caller));
   };
   this.todo_is = function test_todo_is(a, b, name) {
     self.todo(a == b, name, "Got " + a + ", expected " + b,
               Components.stack.caller);
   };
   this.todo_isnot = function test_todo_isnot(a, b, name) {
     self.todo(a != b, name, "Didn't expect " + a + ", but got it",
               Components.stack.caller);
   };
   this.info = function test_info(name) {
-    self.__browserTest.addResult(new testMessage(name));
+    aTest.addResult(new testMessage(name));
   };
 
   this.executeSoon = function test_executeSoon(func) {
     let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
 
     tm.mainThread.dispatch({
       run: function() {
         func();
@@ -433,17 +444,23 @@ function testScope(aTester, aTest) {
     }
 
     try {
       self.__generator.send(arg);
     } catch (ex if ex instanceof StopIteration) {
       // StopIteration means test is finished.
       self.finish();
     } catch (ex) {
-      aTest.addResult(new testResult(false, "Exception thrown", ex, false));
+      var isExpected = !!self.SimpleTest.isExpectingUncaughtException();
+      if (!self.SimpleTest.isIgnoringAllUncaughtExceptions()) {
+        aTest.addResult(new testResult(isExpected, "Exception thrown", ex, false));
+        self.SimpleTest.expectUncaughtException(false);
+      } else {
+        aTest.addResult(new testMessage("Exception thrown: " + ex));
+      }
       self.finish();
     }
   };
 
   this.waitForExplicitFinish = function test_waitForExplicitFinish() {
     self.__done = false;
   };
 
@@ -462,29 +479,26 @@ function testScope(aTester, aTest) {
   this.requestLongerTimeout = function test_requestLongerTimeout(aFactor) {
     self.__timeoutFactor = aFactor;
   };
 
   this.copyToProfile = function test_copyToProfile(filename) {
     self.SimpleTest.copyToProfile(filename);
   };
 
-  this.expectUncaughtException = function test_expectUncaughtException() {
-    self.SimpleTest.expectUncaughtException();
+  this.expectUncaughtException = function test_expectUncaughtException(aExpecting) {
+    self.SimpleTest.expectUncaughtException(aExpecting);
   };
 
-  this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions() {
-    self.SimpleTest.ignoreAllUncaughtExceptions();
+  this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions(aIgnoring) {
+    self.SimpleTest.ignoreAllUncaughtExceptions(aIgnoring);
   };
 
   this.finish = function test_finish() {
     self.__done = true;
-    if (self.SimpleTest._expectingUncaughtException) {
-      self.ok(false, "expectUncaughtException was called but no uncaught exception was detected!");
-    }
     if (self.__waitTimer) {
       self.executeSoon(function() {
         if (self.__done && self.__waitTimer) {
           clearTimeout(self.__waitTimer);
           self.__waitTimer = null;
           self.__tester.nextTest();
         }
       });
--- a/testing/mochitest/chrome/Makefile.in
+++ b/testing/mochitest/chrome/Makefile.in
@@ -42,15 +42,14 @@ VPATH		= @srcdir@
 relativesrcdir  = testing/mochitest/chrome
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _STATIC_FILES = test_sample.xul \
 		test_sanityChromeUtils.xul \
 		test_sanityPluginUtils.html \
-# Disabled until bug 652494 is resolved.
-#		test_sanityException.xul \
-#		test_sanityException2.xul \
+		test_sanityException.xul \
+		test_sanityException2.xul \
 		$(NULL)
 
 libs:: $(_STATIC_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/$(relativesrcdir)
--- a/testing/mochitest/chrome/test_sanityException.xul
+++ b/testing/mochitest/chrome/test_sanityException.xul
@@ -10,14 +10,14 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
 <body xmlns="http://www.w3.org/1999/xhtml">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=670817">Mozilla Bug 670817</a>
 <script type="application/javascript"><![CDATA[
 
 SimpleTest.expectUncaughtException();
 ok(true, "a call to ok");
-throw "uncaught exception";
+throw "this is a deliberately thrown exception";
 
 ]]></script> 
 </body>
 
 </window>
--- a/testing/mochitest/chrome/test_sanityException2.xul
+++ b/testing/mochitest/chrome/test_sanityException2.xul
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body xmlns="http://www.w3.org/1999/xhtml">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=670817">Mozilla Bug 670817</a>
 <script type="application/javascript"><![CDATA[
 
 SimpleTest.waitForExplicitFinish();
 ok(true, "a call to ok");
 SimpleTest.executeSoon(function() {
   SimpleTest.expectUncaughtException();
-  throw "an uncaught exception";
+  throw "this is a deliberately thrown exception";
 });
 SimpleTest.executeSoon(function() {
   SimpleTest.finish();
 });
 
 ]]></script> 
 </body>
 
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -688,27 +688,53 @@ SimpleTest.expectChildProcessCrash = fun
         parentRunner.expectChildProcessCrash();
     }
 };
 
 /**
  * Indicates to the test framework that the next uncaught exception during
  * the test is expected, and should not cause a test failure.
  */
-SimpleTest.expectUncaughtException = function () {
-    SimpleTest._expectingUncaughtException = true;
+SimpleTest.expectUncaughtException = function (aExpecting) {
+    SimpleTest._expectingUncaughtException = aExpecting === void 0 || !!aExpecting;
+};
+
+/**
+ * Returns whether the test has indicated that it expects an uncaught exception
+ * to occur.
+ */
+SimpleTest.isExpectingUncaughtException = function () {
+    return SimpleTest._expectingUncaughtException;
 };
 
 /**
  * Indicates to the test framework that all of the uncaught exceptions
  * during the test are known problems that should be fixed in the future,
  * but which should not cause the test to fail currently.
  */
-SimpleTest.ignoreAllUncaughtExceptions = function () {
-    SimpleTest._ignoringAllUncaughtExceptions = true;
+SimpleTest.ignoreAllUncaughtExceptions = function (aIgnoring) {
+    SimpleTest._ignoringAllUncaughtExceptions = aIgnoring === void 0 || !!aIgnoring;
+};
+
+/**
+ * Returns whether the test has indicated that all uncaught exceptions should be
+ * ignored.
+ */
+SimpleTest.isIgnoringAllUncaughtExceptions = function () {
+    return SimpleTest._ignoringAllUncaughtExceptions;
+};
+
+/**
+ * Resets any state this SimpleTest object has.  This is important for
+ * browser chrome mochitests, which reuse the same SimpleTest object
+ * across a run.
+ */
+SimpleTest.reset = function () {
+    SimpleTest._ignoringAllUncaughtExceptions = false;
+    SimpleTest._expectingUncaughtException = false;
 };
 
 if (isPrimaryTestWindow) {
     addLoadEvent(function() {
         if (SimpleTest._stopOnLoad) {
             SimpleTest.finish();
         }
     });
@@ -919,32 +945,31 @@ var isnot = SimpleTest.isnot;
 var todo = SimpleTest.todo;
 var todo_is = SimpleTest.todo_is;
 var todo_isnot = SimpleTest.todo_isnot;
 var isDeeply = SimpleTest.isDeeply;
 var info = SimpleTest.info;
 
 var gOldOnError = window.onerror;
 window.onerror = function simpletestOnerror(errorMsg, url, lineNumber) {
-    var funcIdentifier = "[SimpleTest/SimpleTest.js, window.onerror]";
-
     // Log the message.
     // XXX Chrome mochitests sometimes trigger this window.onerror handler,
     // but there are a number of uncaught JS exceptions from those tests.
     // For now, for tests that self identify as having unintentional uncaught
     // exceptions, just dump it so that the error is visible but doesn't cause
     // a test failure.  See bug 652494.
-    var message = "An error occurred: " + errorMsg + " at " + url + ":" + lineNumber;
     var href = SpecialPowers.getPrivilegedProps(window, 'location.href');
     var isExpected = !!SimpleTest._expectingUncaughtException;
+    var message = "an " + (isExpected ? "" : "un") + "expected uncaught JS exception reported through window.onerror";
+    var error = errorMsg + " at " + url + ":" + lineNumber;
     if (!SimpleTest._ignoringAllUncaughtExceptions) {
-        SimpleTest.ok(isExpected, funcIdentifier, message);
+        SimpleTest.ok(isExpected, message, error);
         SimpleTest._expectingUncaughtException = false;
     } else {
-        SimpleTest.todo(false, funcIdentifier, message);
+        SimpleTest.todo(false, message + ": " + error);
     }
     // There is no Components.stack.caller to log. (See bug 511888.)
 
     // Call previous handler.
     if (gOldOnError) {
         try {
             // Ignore return value: always run default handler.
             gOldOnError(errorMsg, url, lineNumber);
--- a/testing/mochitest/tests/browser/Makefile.in
+++ b/testing/mochitest/tests/browser/Makefile.in
@@ -47,19 +47,18 @@ include $(topsrcdir)/config/rules.mk
 _BROWSER_TEST_FILES = \
 	                  head.js \
 	                  browser_head.js \
 	                  browser_pass.js \
 	                  browser_async.js \
 	                  browser_privileges.js \
 	                  browser_popupNode.js \
 	                  browser_popupNode_check.js \
-# Disabled until bug 652494 is resolved.
-#			  browser_sanityException.js \
-#			  browser_sanityException2.js \
+			  browser_sanityException.js \
+			  browser_sanityException2.js \
 # Disabled, these are only good for testing the harness' failure reporting
 #	                  browser_zz_fail_openwindow.js \
 #	                  browser_fail.js \
 #	                  browser_fail_async_throw.js \
 #	                  browser_fail_fp.js \
 #	                  browser_fail_pf.js \
 #	                  browser_fail_throw.js \
 #	                  browser_fail_timeout.js \
--- a/testing/mochitest/tests/browser/browser_sanityException.js
+++ b/testing/mochitest/tests/browser/browser_sanityException.js
@@ -1,5 +1,5 @@
 function test() {
   ok(true, "ok called");
   expectUncaughtException();
-  throw "uncaught exception";
+  throw "this is a deliberately thrown exception";
 }
--- a/testing/mochitest/tests/browser/browser_sanityException2.js
+++ b/testing/mochitest/tests/browser/browser_sanityException2.js
@@ -1,11 +1,11 @@
 function test() {
   waitForExplicitFinish();
   ok(true, "ok called");
   executeSoon(function() {
     expectUncaughtException();
-    throw "uncaught exception";
+    throw "this is a deliberately thrown exception";
   });
   executeSoon(function() {
     finish();
   });
 }
--- a/toolkit/components/places/AsyncFaviconHelpers.cpp
+++ b/toolkit/components/places/AsyncFaviconHelpers.cpp
@@ -16,16 +16,17 @@
  * The Original Code is Places.
  *
  * The Initial Developer of the Original Code is the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Marco Bonardo <mak77@bonardo.net> (original author)
+ *   Richard Newman <rnewman@mozilla.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
@@ -719,33 +720,50 @@ AsyncAssociateIconToPage::Run()
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mozStorageTransaction transaction(mDB->MainConn(), false,
                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
   // If there is no entry for this icon, or the entry is obsolete, replace it.
   if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
+    // The 'multi-coalesce' here ensures that replacing a favicon without
+    // specifying a :guid parameter doesn't cause it to be allocated a new
+    // GUID.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
       "INSERT OR REPLACE INTO moz_favicons "
-        "(id, url, data, mime_type, expiration) "
+        "(id, url, data, mime_type, expiration, guid) "
       "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
-              ":icon_url, :data, :mime_type, :expiration) "
+              ":icon_url, :data, :mime_type, :expiration, "
+              "COALESCE(:guid, "
+                       "(SELECT guid FROM moz_favicons "
+                        "WHERE url = :icon_url), "
+                       "GENERATE_GUID()))"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), mIcon.spec);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
                               TO_INTBUFFER(mIcon.data), mIcon.data.Length());
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), mIcon.mimeType);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), mIcon.expiration);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    // Binding a GUID allows us to override the current (or generated) GUID.
+    if (mIcon.guid.IsEmpty()) {
+      rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
+    }
+    else {
+      rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), mIcon.guid);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Get the new icon id.  Do this regardless mIcon.id, since other code
     // could have added a entry before us.  Indeed we interrupted the thread
     // after the previous call to FetchIconInfo.
     rv = FetchIconInfo(mDB, mIcon);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/places/AsyncFaviconHelpers.h
+++ b/toolkit/components/places/AsyncFaviconHelpers.h
@@ -70,25 +70,27 @@ enum AsyncFaviconFetchMode {
 struct IconData
 {
   IconData()
   : id(0)
   , expiration(0)
   , fetchMode(FETCH_NEVER)
   , status(ICON_STATUS_UNKNOWN)
   {
+    guid.SetIsVoid(PR_TRUE);
   }
 
   PRInt64 id;
   nsCString spec;
   nsCString data;
   nsCString mimeType;
   PRTime expiration;
   enum AsyncFaviconFetchMode fetchMode;
   PRUint16 status; // This is a bitset, see ICON_STATUS_* defines above.
+  nsCString guid;
 };
 
 /**
  * Data cache for a page entry.
  */
 struct PageData
 {
   PageData()
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -28,16 +28,17 @@
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
  *   Paolo Amadini <http://www.amadzone.org/>
+ *   Richard Newman <rnewman@mozilla.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
@@ -637,17 +638,23 @@ Database::InitSchema(bool* aDatabaseMigr
       // Firefox 4 uses schema version 11.
 
       // Firefox 8 uses schema version 12.
 
       if (currentSchemaVersion < 13) {
         rv = MigrateV13Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      // Firefox 11 uses schema version 13.
+
+      if (currentSchemaVersion < 14) {
+        rv = MigrateV14Up();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Firefox 11 uses schema version 14.
 
       // Schema Upgrades must add migration code here.
     }
   }
   else {
     // This is a new database, so we have to create all the tables and indices.
 
     // moz_places.
@@ -945,17 +952,17 @@ Database::MigrateV7Up()
   NS_ENSURE_SUCCESS(rv, rv);
   if (!URLUniqueIndexExists) {
     return NS_ERROR_FILE_CORRUPTED;
   }
 
   mozStorageTransaction transaction(mMainConn, false);
 
   // We need an index on lastModified to catch quickly last modified bookmark
-  // title for tag container's children. This will be useful for sync too.
+  // title for tag container's children. This will be useful for Sync, too.
   bool lastModIndexExists = false;
   rv = mMainConn->IndexExists(
     NS_LITERAL_CSTRING("moz_bookmarks_itemlastmodifiedindex"),
     &lastModIndexExists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!lastModIndexExists) {
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
@@ -1277,18 +1284,18 @@ Database::MigrateV11Up()
       "ALTER TABLE moz_bookmarks "
       "ADD COLUMN guid TEXT"
     ));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // moz_placess grew a guid column.  Add the column, but do not populate it
-    // with anything just yet.  We will do that soon.
+    // moz_places grew a guid column. Add the column, but do not populate it
+    // with anything just yet. We will do that soon.
     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
       "ALTER TABLE moz_places "
       "ADD COLUMN guid TEXT"
     ));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1301,35 +1308,65 @@ Database::MigrateV11Up()
   return NS_OK;
 }
 
 nsresult
 Database::MigrateV13Up()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  // Dynamic containers are no more supported.
-
-  // For existing profiles, we may not have a moz_bookmarks.guid column
+  // Dynamic containers are no longer supported.
   nsCOMPtr<mozIStorageAsyncStatement> deleteDynContainersStmt;
   nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
       "DELETE FROM moz_bookmarks WHERE type = :item_type"),
     getter_AddRefs(deleteDynContainersStmt));
   rv = deleteDynContainersStmt->BindInt32ByName(
     NS_LITERAL_CSTRING("item_type"),
     nsINavBookmarksService::TYPE_DYNAMIC_CONTAINER
   );
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<mozIStoragePendingStatement> ps;
   rv = deleteDynContainersStmt->ExecuteAsync(nsnull, getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+nsresult
+Database::MigrateV14Up()
+{
+  // For existing profiles, we may not have a moz_favicons.guid column.
+  // Add it here. We want it to be unique, but ALTER TABLE doesn't allow
+  // a uniqueness constraint, so the index must be created separately.
+  nsCOMPtr<mozIStorageStatement> hasGuidStatement;
+  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+      "SELECT guid FROM moz_favicons"),
+    getter_AddRefs(hasGuidStatement));
+
+  if (NS_FAILED(rv)) {
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "ALTER TABLE moz_favicons "
+      "ADD COLUMN guid TEXT"
+    ));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Generate GUIDs for our existing favicons.
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "UPDATE moz_favicons "
+      "SET guid = GENERATE_GUID()"
+    ));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // And now we can make the column unique.
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_FAVICONS_GUID);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
 void
 Database::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDown);
 
   mMainThreadStatements.FinalizeStatements();
   mMainThreadAsyncStatements.FinalizeStatements();
@@ -1407,16 +1444,20 @@ Database::Observe(nsISupports *aSubject,
       nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
         "SELECT 1 "
         "FROM moz_places "
         "WHERE guid IS NULL "
         "UNION ALL "
         "SELECT 1 "
         "FROM moz_bookmarks "
         "WHERE guid IS NULL "
+        "UNION ALL "
+        "SELECT 1 "
+        "FROM moz_favicons "
+        "WHERE guid IS NULL "
       ), getter_AddRefs(stmt));
       NS_ENSURE_SUCCESS(rv, rv);
 
       bool haveNullGuids;
       rv = stmt->ExecuteStep(&haveNullGuids);
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ASSERTION(!haveNullGuids,
                    "Someone added an entry without adding a GUID!");
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -40,19 +40,19 @@
 
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObserver.h"
 #include "mozilla/storage.h"
 #include "mozilla/storage/StatementCache.h"
 
-// This is the schema version, update it at any schema change and add a
+// This is the schema version. Update it at any schema change and add a
 // corresponding migrateVxx method below.
-#define DATABASE_SCHEMA_VERSION 13
+#define DATABASE_SCHEMA_VERSION 14
 
 // Fired after Places inited.
 #define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
 // Fired when initialization fails due to a locked database.
 #define TOPIC_DATABASE_LOCKED "places-database-locked"
 // This topic is received when the profile is about to be lost.  Places does
 // initial shutdown work and notifies TOPIC_PLACES_SHUTDOWN to all listeners.
 // Any shutdown work that requires the Places APIs should happen here.
@@ -283,18 +283,20 @@ protected:
   /**
    * Helpers used by schema upgrades.
    */
   nsresult MigrateV7Up();
   nsresult MigrateV8Up();
   nsresult MigrateV9Up();
   nsresult MigrateV10Up();
   nsresult MigrateV11Up();
+  nsresult MigrateV13Up();
+  nsresult MigrateV14Up();
+
   nsresult CheckAndUpdateGUIDs();
-  nsresult MigrateV13Up();
 
 private:
   ~Database();
 
   /**
    * Singleton getter, invoked by class instantiation.
    *
    * Note: does AddRef.
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -233,22 +233,25 @@ nsFaviconService::SetFaviconUrlForPage(n
     }
   }
 
   mozStorageTransaction transaction(mDB->MainConn(), false);
 
   if (iconId == -1) {
     // We did not find any entry for this icon, so create a new one.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
-      "INSERT INTO moz_favicons (id, url, data, mime_type, expiration) "
-      "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration)"
+      "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
+      "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
+              "COALESCE(:guid, GENERATE_GUID()))"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
+    rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("data"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("mime_type"));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -438,49 +441,56 @@ nsFaviconService::SetFaviconData(nsIURI*
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasResult) {
       // Get id of the old entry and update it.
       PRInt64 id;
       rv = stmt->GetInt64(0, &id);
       NS_ENSURE_SUCCESS(rv, rv);
       statement = mDB->GetStatement(
-        "UPDATE moz_favicons SET data = :data, mime_type = :mime_type, "
-                                "expiration = :expiration "
+        "UPDATE moz_favicons SET "
+               "guid       = COALESCE(:guid, guid), "
+               "data       = :data, "
+               "mime_type  = :mime_type, "
+               "expiration = :expiration "
         "WHERE id = :icon_id"
       );
       NS_ENSURE_STATE(statement);
 
+      rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
+      NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), id);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
       // Insert a new entry.
       statement = mDB->GetStatement(
-        "INSERT INTO moz_favicons (id, url, data, mime_type, expiration) "
-        "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration)"
-      );
+       "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
+       "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
+               "COALESCE(:guid, GENERATE_GUID()))");
       NS_ENSURE_STATE(statement);
 
       rv = statement->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
       NS_ENSURE_SUCCESS(rv, rv);
       rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
       NS_ENSURE_SUCCESS(rv, rv);
+      rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
+      NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   mozStorageStatementScoper statementScoper(statement);
 
   rv = statement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -25,16 +25,17 @@
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
  *   Paolo Amadini <http://www.amadzone.org/>
+ *   Richard Newman <rnewman@mozilla.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
--- a/toolkit/components/places/nsPlacesIndexes.h
+++ b/toolkit/components/places/nsPlacesIndexes.h
@@ -140,10 +140,18 @@
  * moz_items_annos
  */
 
 #define CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE \
   CREATE_PLACES_IDX( \
     "itemattributeindex", "moz_items_annos", "item_id, anno_attribute_id", "UNIQUE" \
   )
 
+/**
+ * moz_favicons
+ */
+
+#define CREATE_IDX_MOZ_FAVICONS_GUID \
+  CREATE_PLACES_IDX( \
+    "guid_uniqueindex", "moz_favicons", "guid", "UNIQUE" \
+  )
 
 #endif // nsPlacesIndexes_h__
--- a/toolkit/components/places/nsPlacesTables.h
+++ b/toolkit/components/places/nsPlacesTables.h
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Richard Newman <rnewman@mozilla.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
@@ -122,16 +123,17 @@
 
 #define CREATE_MOZ_FAVICONS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_favicons (" \
     "  id INTEGER PRIMARY KEY" \
     ", url LONGVARCHAR UNIQUE" \
     ", data BLOB" \
     ", mime_type VARCHAR(32)" \
     ", expiration LONG" \
+    ", guid TEXT" \
   ")" \
 )
 
 #define CREATE_MOZ_BOOKMARKS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_bookmarks (" \
     "  id INTEGER PRIMARY KEY" \
     ", type INTEGER" \
     ", fk INTEGER DEFAULT NULL" /* place_id */ \
--- a/toolkit/components/places/tests/cpp/mock_Link.h
+++ b/toolkit/components/places/tests/cpp/mock_Link.h
@@ -52,35 +52,44 @@ public:
   NS_DECL_ISUPPORTS
 
   mock_Link(void (*aHandlerFunction)(nsLinkState),
             bool aRunNextTest = true)
   : mozilla::dom::Link(nsnull)
   , mHandler(aHandlerFunction)
   , mRunNextTest(aRunNextTest)
   {
+    // Create a cyclic ownership, so that the link will be released only
+    // after its status has been updated.  This will ensure that, when it should
+    // run the next test, it will happen at the end of the test function, if
+    // the link status has already been set before.  Indeed the link status is
+    // updated on a separate connection, thus may happen at any time.
+    mDeathGrip = this;
   }
 
   virtual void SetLinkState(nsLinkState aState)
   {
     // Notify our callback function.
     mHandler(aState);
 
+    // Break the cycle so the object can be destroyed.
+    mDeathGrip = 0;
+  }
+
+  ~mock_Link() {
     // Run the next test if we are supposed to.
     if (mRunNextTest) {
       run_next_test();
     }
-
-    // Finally, we must manually release ourselves.
-    NS_RELEASE_THIS();
   }
 
 private:
   void (*mHandler)(nsLinkState);
   bool mRunNextTest;
+  nsRefPtr<Link> mDeathGrip;
 };
 
 NS_IMPL_ISUPPORTS1(
   mock_Link,
   mozilla::dom::Link
 )
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/toolkit/components/places/tests/cpp/test_IHistory.cpp
+++ b/toolkit/components/places/tests/cpp/test_IHistory.cpp
@@ -188,17 +188,16 @@ test_visited_notifies()
   // have on the Link.
   nsRefPtr<Link> link = new mock_Link(expect_visit);
 
   // Now, register our Link to be notified.
   nsCOMPtr<IHistory> history = do_get_IHistory();
   nsresult rv = history->RegisterVisitedCallback(testURI, link);
   do_check_success(rv);
 
-  link.forget(); // It will release itself when notified.
   // Note: test will continue upon notification.
 }
 
 void
 test_unvisited_does_not_notify_part2()
 {
   using namespace test_unvisited_does_not_notify;
 
@@ -231,18 +230,16 @@ test_same_uri_notifies_both()
 
   // Now, register our Link to be notified.
   nsCOMPtr<IHistory> history = do_get_IHistory();
   nsresult rv = history->RegisterVisitedCallback(testURI, link1);
   do_check_success(rv);
   rv = history->RegisterVisitedCallback(testURI, link2);
   do_check_success(rv);
 
-  link1.forget(); // It will release itself when notified.
-  link2.forget(); // It will release itself when notified.
   // Note: test will continue upon notification.
 }
 
 void
 test_unregistered_visited_does_not_notify()
 {
   // This test must have a test that has a successful notification after it.
   // The Link would have been notified by now if we were buggy and notified
@@ -283,17 +280,16 @@ test_new_visit_notifies_waiting_Link()
   nsCOMPtr<nsIURI> testURI = new_test_uri();
   nsCOMPtr<IHistory> history = do_get_IHistory();
   nsresult rv = history->RegisterVisitedCallback(testURI, link);
   do_check_success(rv);
 
   // Add ourselves to history.
   addURI(testURI);
 
-  link.forget(); // It will release itself when notified.
   // Note: test will continue upon notification.
 }
 
 void
 test_RegisterVisitedCallback_returns_before_notifying()
 {
   // Add a URI so that it's already in history.
   nsCOMPtr<nsIURI> testURI = new_test_uri();
@@ -397,17 +393,16 @@ test_observer_topic_dispatched()
   nsresult rv = visitedURI->Equals(notVisitedURI, &urisEqual);
   do_check_success(rv);
   do_check_false(urisEqual);
   addURI(visitedURI);
 
   // Need two Link objects as well - one for each URI.
   nsRefPtr<Link> visitedLink = new mock_Link(expect_visit, false);
   nsRefPtr<Link> visitedLinkCopy = visitedLink;
-  visitedLinkCopy.forget(); // It will release itself when notified.
   nsRefPtr<Link> notVisitedLink = new mock_Link(expect_no_visit);
 
   // Add the right observers for the URIs to check results.
   bool visitedNotified = false;
   nsCOMPtr<nsIObserver> visitedObs =
     new statusObserver(visitedURI, true, visitedNotified);
   bool notVisitedNotified = false;
   nsCOMPtr<nsIObserver> unvisitedObs =
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -30,17 +30,17 @@
  * 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 ***** */
 
-const CURRENT_SCHEMA_VERSION = 13;
+const CURRENT_SCHEMA_VERSION = 14;
 
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
 const NS_APP_BOOKMARKS_50_FILE = "BMarks";
 
 // Shortcuts to transitions type.
 const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
 const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
--- a/toolkit/components/places/tests/migration/test_current_from_v10.js
+++ b/toolkit/components/places/tests/migration/test_current_from_v10.js
@@ -290,16 +290,17 @@ function test_final_state()
     do_check_true(stmt.executeStep());
     // WAL journal mode should be set on this database.
     do_check_eq(stmt.getString(0).toLowerCase(), "wal");
     stmt.finalize();
   }
 
   do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex"));
   do_check_true(db.indexExists("moz_places_guid_uniqueindex"));
+  do_check_true(db.indexExists("moz_favicons_guid_uniqueindex"));
 
   do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
 
   db.close();
   run_next_test();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/toolkit/components/places/tests/unit/test_favicons.js
+++ b/toolkit/components/places/tests/unit/test_favicons.js
@@ -83,16 +83,27 @@ function check_oversized_icon_data(iconN
 
   // Compare thet expected data to the actual data.
   do_check_eq("image/png", outMimeType);
   if (!skipContent) {
     do_check_true(compareArrays(expectedData, outData));
   }
 }
 
+function check_page_has_no_favicon(pageURI) {
+  try {
+    PlacesUtils.favicons.getFaviconForPage(pageURI);
+    do_throw("Page has a favicon!");
+  } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) {
+    // Page should have no favicon.
+  } catch (ex) {
+    do_throw("Unexpected exception " + ex);
+  }
+}
+
 /*
  * Done with utilities! On to the tests.
  */
 function run_test() {
   run_next_test();
 }
 
 add_test(function test_storing_a_normal_16x16_icon() {
@@ -343,8 +354,150 @@ add_test(function test_getFaviconData_on
   do_check_eq(outMimeType.value, "image/png");
 
   // Read in the icon and compare it to what the API returned above.
   let istream = NetUtil.newChannel(PlacesUtils.favicons.defaultFavicon).open();
   let expectedData = readInputStreamData(istream);
   do_check_true(compareArrays(outData, expectedData));
   run_next_test();
 });
+
+/*
+ * Retrieve the GUID for a favicon URI. For now we'll do this through SQL.
+ * Provide a mozIStorageStatementCallback, such as a SingleGUIDCallback.
+ */
+function guidForFaviconURI(iconURIString, cb) {
+  let query = "SELECT guid FROM moz_favicons WHERE url = :url";
+  let stmt = cb.statement = DBConn().createAsyncStatement(query);
+  stmt.params.url = iconURIString;
+  stmt.executeAsync(cb);
+  stmt.finalize();
+}
+
+/*
+ * A mozIStorageStatementCallback for single GUID results.
+ * Pass in a callback function, which will be invoked on completion.
+ * Ensures that only a single GUID is returned.
+ */
+function SingleGUIDCallback(cb) {
+  this.called = 0;
+  this.cb     = cb;
+}
+SingleGUIDCallback.prototype = {
+  _guid: null,
+  handleCompletion: function handleCompletion(reason) {
+    do_log_info("Completed single GUID callback.");
+    do_check_eq(this.called, 1);
+    this.cb(this._guid);
+  },
+  handleError: function handleError(err) {
+    do_throw(err);
+  },
+  handleResult: function handleResult(resultSet) {
+    this.called++;
+    this._guid = resultSet.getNextRow().getResultByName("guid");
+    do_log_info("Retrieved GUID is " + this._guid);
+    do_check_true(!!this._guid);
+    do_check_valid_places_guid(this._guid);
+    do_check_eq(null, resultSet.getNextRow());  // No more rows.
+  }
+};
+
+function insertToolbarBookmark(uri, title) {
+  PlacesUtils.bookmarks.insertBookmark(
+    PlacesUtils.toolbarFolderId,
+    uri,
+    PlacesUtils.bookmarks.DEFAULT_INDEX,
+    title
+  );
+}
+
+add_test(function test_insert_synchronous_mints_guid() {
+  do_log_info("Test that synchronously inserting a favicon results in a " +
+              "record with a new GUID.");
+
+  let testURI     = "http://test.com/sync/";
+  let testIconURI = "http://test.com/favicon.ico";
+  let pageURI     = NetUtil.newURI(testURI);
+
+  // No icon to start with.
+  check_page_has_no_favicon(pageURI);
+
+  // Add a page with a bookmark.
+  insertToolbarBookmark(pageURI, "Test page");
+
+  // Set a favicon for the page.
+  PlacesUtils.favicons.setFaviconUrlForPage(
+    pageURI, NetUtil.newURI(testIconURI)
+  );
+
+  // Check that the URI has been set correctly.
+  do_check_eq(PlacesUtils.favicons.getFaviconForPage(pageURI).spec,
+              testIconURI);
+
+  guidForFaviconURI(testIconURI, new SingleGUIDCallback(run_next_test));
+});
+
+add_test(function test_insert_asynchronous_mints_guid() {
+  do_log_info("Test that asynchronously inserting a favicon results in a " +
+              "record with a new GUID.");
+
+  let testURI = "http://test.com/async/";
+  let iconURI = NetUtil.newURI(do_get_file("favicon-normal32.png"));
+  let pageURI = NetUtil.newURI(testURI);
+
+  // No icon to start with.
+  check_page_has_no_favicon(pageURI);
+
+  // Add a page with a bookmark.
+  insertToolbarBookmark(pageURI, "Other test page");
+
+  // Set a favicon for the page.
+  let faviconDataCallback = {
+    onFaviconDataAvailable: function (uri, len, data, mimeType) {
+      do_check_true(iconURI.equals(uri));
+
+      // Make sure there's a valid GUID.
+      guidForFaviconURI(iconURI.spec, new SingleGUIDCallback(run_next_test));
+    }
+  };
+
+  let forceReload = false;
+  do_log_info("Asynchronously setting page favicon.");
+  PlacesUtils.favicons.setAndFetchFaviconForPage(
+    pageURI, iconURI, forceReload, faviconDataCallback
+  );
+});
+
+add_test(function test_insert_asynchronous_update_preserves_guid() {
+  do_log_info("Test that asynchronously inserting an existing favicon leaves " +
+              "the GUID unchanged.");
+
+  let testURI = "http://test.com/async/";
+  let iconURI = NetUtil.newURI(do_get_file("favicon-normal32.png"));
+  let pageURI = NetUtil.newURI(testURI);
+
+  guidForFaviconURI(iconURI.spec, new SingleGUIDCallback(function (guid) {
+    // Set a favicon for the page... again.
+    let faviconDataCallback = {
+      onFaviconDataAvailable: function (uri, len, data, mimeType) {
+        do_check_true(iconURI.equals(uri));
+
+        // Make sure there's a valid GUID.
+        guidForFaviconURI(iconURI.spec, new SingleGUIDCallback(function (again) {
+          do_check_eq(guid, again);
+          run_next_test();
+        }));
+      }
+    };
+
+    let forceReload = true;
+    do_log_info("Asynchronously re-setting page favicon.");
+    PlacesUtils.favicons.setAndFetchFaviconForPage(
+      pageURI, iconURI, forceReload, faviconDataCallback
+    );
+  }));
+});
+
+/*
+ * TODO: need a test for async write that modifies a GUID for a favicon.
+ * This will come later, when there's an API that actually does that!
+ */
--- a/toolkit/components/startup/tests/browser/browser_bug511456.js
+++ b/toolkit/components/startup/tests/browser/browser_bug511456.js
@@ -83,16 +83,17 @@ var Watcher = {
       return this;
 
     throw Components.results.NS_ERROR_NO_INTERFACE;
   }
 }
 
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   Services.wm.addListener(Watcher);
 
   var win2 = window.openDialog(location, "", "chrome,all,dialog=no", "about:blank");
   win2.addEventListener("load", function() {
     win2.removeEventListener("load", arguments.callee, false);
 
     gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -212,29 +212,37 @@ JSHistogram_Add(JSContext *cx, uintN arg
   }
 
   if (!JS_ValueToECMAInt32(cx, v, &value)) {
     return JS_FALSE;
   }
 
   if (TelemetryImpl::CanRecord()) {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj) {
+      return JS_FALSE;
+    }
+
     Histogram *h = static_cast<Histogram*>(JS_GetPrivate(cx, obj));
     if (h->histogram_type() == Histogram::BOOLEAN_HISTOGRAM)
       h->Add(!!value);
     else
       h->Add(value);
   }
   return JS_TRUE;
 }
 
 JSBool
 JSHistogram_Snapshot(JSContext *cx, uintN argc, jsval *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
+  if (!obj) {
+    return JS_FALSE;
+  }
+
   Histogram *h = static_cast<Histogram*>(JS_GetPrivate(cx, obj));
   JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
   if (!snapshot)
     return JS_FALSE;
   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(snapshot));
   return ReflectHistogramSnapshot(cx, snapshot, h);
 }
 
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -157,16 +157,34 @@ HISTOGRAM(HTTP_REQUEST_PER_PAGE_FROM_CAC
   _HTTP_HIST(HTTP_##prefix##_REVALIDATION, labelprefix "Positive cache validation time (ms)") \
   _HTTP_HIST(HTTP_##prefix##_COMPLETE_LOAD, labelprefix "Overall load time - all (ms)") \
   _HTTP_HIST(HTTP_##prefix##_COMPLETE_LOAD_CACHED, labelprefix "Overall load time - cache hits (ms)") \
   _HTTP_HIST(HTTP_##prefix##_COMPLETE_LOAD_NET, labelprefix "Overall load time - network (ms)") \
 
 HTTP_HISTOGRAMS(PAGE, "page: ")
 HTTP_HISTOGRAMS(SUB, "subitem: ")
 
+HISTOGRAM(SPDY_PARALLEL_STREAMS, 1, 1000, 50, EXPONENTIAL, "SPDY: Streams concurrent active per connection")
+HISTOGRAM(SPDY_TOTAL_STREAMS, 1, 100000, 50, EXPONENTIAL,  "SPDY: Streams created per connection")
+HISTOGRAM(SPDY_SERVER_INITIATED_STREAMS, 1, 100000, 250, EXPONENTIAL,  "SPDY: Streams recevied per connection")
+HISTOGRAM(SPDY_CHUNK_RECVD, 1, 1000, 100, EXPONENTIAL,  "SPDY: Recvd Chunk Size (rounded to KB)")
+HISTOGRAM(SPDY_SYN_SIZE, 20, 20000, 50, EXPONENTIAL,  "SPDY: SYN Frame Header Size")
+HISTOGRAM(SPDY_SYN_RATIO, 1, 99, 20, LINEAR,  "SPDY: SYN Frame Header Ratio (lower better)")
+HISTOGRAM(SPDY_SYN_REPLY_SIZE, 16, 20000, 50, EXPONENTIAL,  "SPDY: SYN Reply Header Size")
+HISTOGRAM(SPDY_SYN_REPLY_RATIO, 1, 99, 20, LINEAR,  "SPDY: SYN Reply Header Ratio (lower better)")
+HISTOGRAM(SPDY_NPN_CONNECT, 0, 1, 2, BOOLEAN,  "SPDY: NPN Negotiated")
+
+HISTOGRAM(SPDY_SETTINGS_UL_BW, 1, 10000, 100, EXPONENTIAL,  "SPDY: Settings Upload Bandwidth")
+HISTOGRAM(SPDY_SETTINGS_DL_BW, 1, 10000, 100, EXPONENTIAL,  "SPDY: Settings Download Bandwidth")
+HISTOGRAM(SPDY_SETTINGS_RTT, 1, 1000, 100, EXPONENTIAL,  "SPDY: Settings RTT")
+HISTOGRAM(SPDY_SETTINGS_MAX_STREAMS, 1, 5000, 100, EXPONENTIAL,  "SPDY: Settings Max Streams parameter")
+HISTOGRAM(SPDY_SETTINGS_CWND, 1, 500, 50, EXPONENTIAL,  "SPDY: Settings CWND (packets)")
+HISTOGRAM(SPDY_SETTINGS_RETRANS, 1, 100, 50, EXPONENTIAL,  "SPDY: Retransmission Rate")
+HISTOGRAM(SPDY_SETTINGS_IW, 1, 1000, 50, EXPONENTIAL,  "SPDY: Settings IW (rounded to KB)")
+
 #undef _HTTP_HIST
 #undef HTTP_HISTOGRAMS
 
 HISTOGRAM(HTTP_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Cache Hit, Reval, Failed-Reval, Miss")
 HISTOGRAM(HTTP_DISK_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Disk Cache Hit, Reval, Failed-Reval, Miss")
 HISTOGRAM(HTTP_MEMORY_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Memory Cache Hit, Reval, Failed-Reval, Miss")
 HISTOGRAM(HTTP_OFFLINE_CACHE_DISPOSITION, 1, 5, 5, LINEAR, "HTTP Offline Cache Hit, Reval, Failed-Reval, Miss")
 HISTOGRAM(CACHE_DEVICE_SEARCH, 1, 100, 100, LINEAR, "Time to search cache (ms)")
@@ -212,16 +230,19 @@ HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE
 HISTOGRAM(MOZ_SQLITE_WEBAPPS_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_MS, 1, 32768, 20, EXPONENTIAL, "mozStorage async requests completion (ms)")
 HISTOGRAM_BOOLEAN(MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, "mozStorage async requests success")
 HISTOGRAM(STARTUP_MEASUREMENT_ERRORS, 1, mozilla::StartupTimeline::MAX_EVENT_ID, mozilla::StartupTimeline::MAX_EVENT_ID + 1, LINEAR, "Flags errors in startup calculation()")
 HISTOGRAM(NETWORK_DISK_CACHE_OPEN, 1, 10000, 10, EXPONENTIAL, "Time spent opening disk cache (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_TRASHRENAME, 1, 10000, 10, EXPONENTIAL, "Time spent renaming bad Cache to Cache.Trash (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_DELETEDIR, 1, 10000, 10, EXPONENTIAL, "Time spent deleting disk cache (ms)")
+HISTOGRAM(NETWORK_DISK_CACHE_DELETEDIR_SHUTDOWN, 1, 10000, 10, EXPONENTIAL, "Time spent during showdown stopping thread deleting old disk cache (ms)")
+HISTOGRAM(NETWORK_DISK_CACHE_SHUTDOWN, 1, 10000, 10, EXPONENTIAL, "Total Time spent (ms) during disk cache showdown")
+HISTOGRAM(NETWORK_DISK_CACHE_SHUTDOWN_CLEAR_PRIVATE, 1, 10000, 10, EXPONENTIAL, "Time spent (ms) during showdown deleting disk cache for 'clear private data' option")
 
 /**
  * Url-Classifier telemetry
  */
 #ifdef MOZ_URL_CLASSIFIER
 HISTOGRAM(URLCLASSIFIER_PS_FILELOAD_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent loading PrefixSet from file (ms)")
 HISTOGRAM(URLCLASSIFIER_PS_FALLOCATE_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent fallocating PrefixSet (ms)")
 HISTOGRAM(URLCLASSIFIER_PS_CONSTRUCT_TIME, 1, 5000, 15, EXPONENTIAL, "Time spent constructing PrefixSet from DB (ms)")
@@ -254,10 +275,27 @@ HISTOGRAM(THUNDERBIRD_CONVERSATIONS_TIME
 HISTOGRAM(THUNDERBIRD_INDEXING_RATE_MSG_PER_S, 1, 100, 20, LINEAR, "Gloda: indexing rate (message/s)")
 #endif
 
 HISTOGRAM_BOOLEAN(INNERWINDOWS_WITH_MUTATION_LISTENERS, "Deleted or to-be-reused innerwindow which has had mutation event listeners.")
 HISTOGRAM(XUL_REFLOW_MS, 1, 3000, 10, EXPONENTIAL, "xul reflows")
 HISTOGRAM(XUL_INITIAL_FRAME_CONSTRUCTION, 1, 3000, 10, EXPONENTIAL, "initial xul frame construction")
 HISTOGRAM_BOOLEAN(XMLHTTPREQUEST_ASYNC_OR_SYNC, "Type of XMLHttpRequest, async or sync")
 
+/**
+ * DOM Storage telemetry.
+ */
+#define DOMSTORAGE_HISTOGRAM(PREFIX, TYPE, TYPESTRING, DESCRIPTION) \
+  HISTOGRAM(PREFIX ## DOMSTORAGE_ ## TYPE ## _SIZE_BYTES, \
+            1024, 32768, 10, EXPONENTIAL, "DOM storage: size of " TYPESTRING "s stored in " DESCRIPTION "Storage")
+#define DOMSTORAGE_KEY_VAL_SIZE(PREFIX, DESCRIPTION) \
+  DOMSTORAGE_HISTOGRAM(PREFIX, KEY, "key", DESCRIPTION) \
+  DOMSTORAGE_HISTOGRAM(PREFIX, VALUE, "value", DESCRIPTION)
+
+DOMSTORAGE_KEY_VAL_SIZE(GLOBAL, "global")
+DOMSTORAGE_KEY_VAL_SIZE(LOCAL, "local")
+DOMSTORAGE_KEY_VAL_SIZE(SESSION, "session")
+
+#undef DOMSTORAGE_KEY_VAL_SIZE
+#undef DOMSTORAGE_HISTOGRAM
+
 
 #undef HISTOGRAM_BOOLEAN
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -57,17 +57,16 @@
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefBranch2.h"
 #include "nsIPrefService.h"
 #include "nsIProperties.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIUrlClassifierUtils.h"
-#include "nsIRandomGenerator.h"
 #include "nsUrlClassifierDBService.h"
 #include "nsUrlClassifierUtils.h"
 #include "nsUrlClassifierProxies.h"
 #include "nsURILoader.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsNetUtil.h"
@@ -483,19 +482,23 @@ public:
 
   // Read a certain number of rows adjacent to the requested rowid that
   // don't have complete hash data.
   nsresult ReadNoiseEntries(PRInt64 rowID,
                             PRUint32 numRequested,
                             bool before,
                             nsTArray<nsUrlClassifierEntry> &entries);
 
+  // Ask the db for a random number.  This is temporary, and should be
+  // replaced with nsIRandomGenerator when 419739 is fixed.
+  nsresult RandomNumber(PRInt64 *randomNum);
   // Return an array with all Prefixes known
   nsresult ReadPrefixes(nsTArray<PRUint32>& array, PRUint32 aKey);
 
+
 protected:
   nsresult ReadEntries(mozIStorageStatement *statement,
                        nsTArray<nsUrlClassifierEntry>& entries);
   nsUrlClassifierDBServiceWorker *mWorker;
   nsCOMPtr<mozIStorageConnection> mConnection;
 
   nsCOMPtr<mozIStorageStatement> mLookupWithIDStatement;
 
@@ -504,16 +507,17 @@ protected:
   nsCOMPtr<mozIStorageStatement> mDeleteStatement;
   nsCOMPtr<mozIStorageStatement> mExpireStatement;
 
   nsCOMPtr<mozIStorageStatement> mPartialEntriesStatement;
   nsCOMPtr<mozIStorageStatement> mPartialEntriesAfterStatement;
   nsCOMPtr<mozIStorageStatement> mLastPartialEntriesStatement;
   nsCOMPtr<mozIStorageStatement> mPartialEntriesBeforeStatement;
 
+  nsCOMPtr<mozIStorageStatement> mRandomStatement;
   nsCOMPtr<mozIStorageStatement> mAllPrefixStatement;
 };
 
 nsresult
 nsUrlClassifierStore::Init(nsUrlClassifierDBServiceWorker *worker,
                            mozIStorageConnection *connection,
                            const nsACString& entriesName)
 {
@@ -562,17 +566,21 @@ nsUrlClassifierStore::Init(nsUrlClassifi
   rv = mConnection->CreateStatement
     (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName +
      NS_LITERAL_CSTRING(" WHERE id < ?1 AND complete_data ISNULL"
                         " ORDER BY id DESC LIMIT ?2"),
      getter_AddRefs(mPartialEntriesBeforeStatement));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mConnection->CreateStatement
-    (NS_LITERAL_CSTRING("SELECT domain, partial_data, complete_data FROM ")
+    (NS_LITERAL_CSTRING("SELECT abs(random())"),
+     getter_AddRefs(mRandomStatement));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT domain, partial_data, complete_data FROM ")
      + entriesName,
      getter_AddRefs(mAllPrefixStatement));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
@@ -584,16 +592,17 @@ nsUrlClassifierStore::Close()
   mUpdateStatement = nsnull;
   mDeleteStatement = nsnull;
   mExpireStatement = nsnull;
 
   mPartialEntriesStatement = nsnull;
   mPartialEntriesAfterStatement = nsnull;
   mPartialEntriesBeforeStatement = nsnull;
   mLastPartialEntriesStatement = nsnull;
+  mRandomStatement = nsnull;
 
   mAllPrefixStatement = nsnull;
 
   mConnection = nsnull;
 }
 
 
 bool
@@ -768,16 +777,31 @@ nsUrlClassifierStore::ReadNoiseEntries(P
   mozStorageStatementScoper wraparoundScoper(wraparoundStatement);
 
   rv = wraparoundStatement->BindInt32ByIndex(0, numRequested - numRead);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return ReadEntries(wraparoundStatement, entries);
 }
 
+nsresult
+nsUrlClassifierStore::RandomNumber(PRInt64 *randomNum)
+{
+  mozStorageStatementScoper randScoper(mRandomStatement);
+  bool exists;
+  nsresult rv = mRandomStatement->ExecuteStep(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!exists)
+    return NS_ERROR_NOT_AVAILABLE;
+
+  *randomNum = mRandomStatement->AsInt64(0);
+
+  return NS_OK;
+}
+
 // -------------------------------------------------------------------------
 // nsUrlClassifierAddStore class implementation
 
 // This class accesses the moz_classifier table.
 class nsUrlClassifierAddStore: public nsUrlClassifierStore
 {
 public:
   nsUrlClassifierAddStore() {};
@@ -1762,26 +1786,19 @@ nsresult
 nsUrlClassifierDBServiceWorker::AddNoise(PRInt64 nearID,
                                          PRInt32 count,
                                          nsTArray<nsUrlClassifierLookupResult>& results)
 {
   if (count < 1) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIRandomGenerator> rg =
-    do_GetService("@mozilla.org/security/random-generator;1");
-  NS_ENSURE_STATE(rg);
-
-  PRInt32 randomNum;
-  PRUint8 *temp;
-  nsresult rv = rg->GenerateRandomBytes(sizeof(randomNum), &temp);
+  PRInt64 randomNum;
+  nsresult rv = mMainStore.RandomNumber(&randomNum);
   NS_ENSURE_SUCCESS(rv, rv);
-  memcpy(&randomNum, temp, sizeof(randomNum));
-  NS_Free(temp);
 
   PRInt32 numBefore = randomNum % count;
 
   nsTArray<nsUrlClassifierEntry> noiseEntries;
   rv = mMainStore.ReadNoiseEntries(nearID, numBefore, true, noiseEntries);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mMainStore.ReadNoiseEntries(nearID, count - numBefore, false, noiseEntries);
@@ -3073,18 +3090,23 @@ nsUrlClassifierDBServiceWorker::FinishSt
            mServerMAC.get(), clientMAC.get()));
       mUpdateStatus = NS_ERROR_FAILURE;
     }
     PRIntervalTime updateTime = PR_IntervalNow() - mUpdateStartTime;
     if (PR_IntervalToSeconds(updateTime) >=
         static_cast<PRUint32>(gWorkingTimeThreshold)) {
       // We've spent long enough working that we should commit what we
       // have and hold off for a bit.
-      ApplyUpdate();
-
+      nsresult rv = ApplyUpdate();
+      if (NS_FAILED(rv)) {
+        if (rv == NS_ERROR_FILE_CORRUPTED) {
+          ResetDatabase();
+        }
+        return rv;
+      }
       nextStreamDelay = gDelayTime * 1000;
     }
   }
 
   mUpdateObserver->StreamFinished(mUpdateStatus,
                                   static_cast<PRUint32>(nextStreamDelay));
 
   ResetStream();
@@ -3186,17 +3208,23 @@ nsUrlClassifierDBServiceWorker::FinishUp
   NS_ENSURE_STATE(mUpdateObserver);
 
   // We need to get the error code before ApplyUpdate, because it might
   // close/open the connection.
   PRInt32 errcode = SQLITE_OK;
   if (mConnection)
     mConnection->GetLastError(&errcode);
 
-  ApplyUpdate();
+  nsresult rv = ApplyUpdate();
+  if (NS_FAILED(rv)) {
+    if (rv == NS_ERROR_FILE_CORRUPTED) {
+      ResetDatabase();
+    }
+    return rv;
+  }
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
     mUpdateObserver->UpdateSuccess(mUpdateWait);
   } else {
     mUpdateObserver->UpdateError(mUpdateStatus);
   }
 
   // It's important that we only reset the database on an update
@@ -3448,17 +3476,22 @@ nsUrlClassifierDBServiceWorker::OpenDb()
 
   mConnection = connection;
 
   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   LOG(("loading Prefix Set\n"));
   rv = LoadPrefixSet(mPSFile);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    if (rv == NS_ERROR_FILE_CORRUPTED) {
+      ResetDatabase();
+    }
+    return rv;
+  }
 
   return NS_OK;
 }
 
 // We have both a prefix and a domain. Drop the domain, but
 // hash the domain, the prefix and a random value together,
 // ensuring any collisions happens at a different points for
 // different users.
@@ -3556,16 +3589,21 @@ nsresult nsUrlClassifierStore::ReadPrefi
     }
 
     PRUint32 keyedVal;
     nsresult rv = KeyedHash(prefixval, domainval, aKey, &keyedVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     array.AppendElement(keyedVal);
     pcnt++;
+    // Normal DB size is about 500k entries. If we are getting 10x
+    // as much, the database must be corrupted.
+    if (pcnt > 5000000) {
+      return NS_ERROR_FILE_CORRUPTED;
+    }
   }
 
   LOG(("SB prefixes: %d fulldomain: %d\n", pcnt, fcnt));
 
 #if defined(PR_LOGGING)
   if (LOG_ENABLED()) {
     PRIntervalTime clockEnd = PR_IntervalNow();
     LOG(("Gathering took %dms\n",
@@ -3643,17 +3681,18 @@ nsUrlClassifierDBServiceWorker::LoadPref
 
   if (exists) {
     Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
     LOG(("stored PrefixSet exists, loading from disk"));
     rv = mPrefixSet->LoadFromFile(aFile);
   }
   if (!exists || NS_FAILED(rv)) {
     LOG(("no (usable) stored PrefixSet found, constructing from store"));
-    ConstructPrefixSet();
+    rv = ConstructPrefixSet();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
 #ifdef DEBUG
   PRUint32 size = 0;
   rv = mPrefixSet->SizeOfIncludingThis(&size);
   LOG(("SB tree done, size = %d bytes\n", size));
   NS_ENSURE_SUCCESS(rv, rv);
 #endif
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -307,18 +307,25 @@ nsUrlClassifierPrefixSet::Contains(PRUin
   PRUint32 i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
   if (mIndexPrefixes[i] > target && i > 0) {
     i--;
   }
 
   // Now search through the deltas for the target.
   PRUint32 diff = target - mIndexPrefixes[i];
   PRUint32 deltaIndex = mIndexStarts[i];
+  PRUint32 deltaSize  = mDeltas.Length();
   PRUint32 end = (i + 1 < mIndexStarts.Length()) ? mIndexStarts[i+1]
-                                                 : mDeltas.Length();
+                                                 : deltaSize;
+
+  // Sanity check the read values
+  if (end > deltaSize) {
+    return NS_ERROR_FILE_CORRUPTED;
+  }
+
   while (diff > 0 && deltaIndex < end) {
     diff -= mDeltas[deltaIndex];
     deltaIndex++;
   }
 
   if (diff == 0) {
     *aFound = true;
   }
@@ -397,49 +404,55 @@ nsUrlClassifierPrefixSet::Probe(PRUint32
 
 nsresult
 nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
 {
   PRUint32 magic;
   PRInt32 read;
 
   read = PR_Read(fileFd, &magic, sizeof(PRUint32));
-  NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FAILURE);
 
   if (magic == PREFIXSET_VERSION_MAGIC) {
     PRUint32 indexSize;
     PRUint32 deltaSize;
 
     read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32));
-    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
     read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
-    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
     read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
-    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED);
 
     if (indexSize == 0) {
       LOG(("stored PrefixSet is empty!"));
       return NS_ERROR_FAILURE;
     }
 
+    if (deltaSize > (indexSize * DELTAS_LIMIT)) {
+      return NS_ERROR_FILE_CORRUPTED;
+    }
+
     nsTArray<PRUint32> mNewIndexPrefixes;
     nsTArray<PRUint32> mNewIndexStarts;
     nsTArray<PRUint16> mNewDeltas;
 
     mNewIndexStarts.SetLength(indexSize);
     mNewIndexPrefixes.SetLength(indexSize);
     mNewDeltas.SetLength(deltaSize);
 
-    read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), indexSize*sizeof(PRUint32));
-    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
-    read = PR_Read(fileFd, mNewIndexStarts.Elements(), indexSize*sizeof(PRUint32));
-    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    PRInt32 toRead = indexSize*sizeof(PRUint32);
+    read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), toRead);
+    NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
+    read = PR_Read(fileFd, mNewIndexStarts.Elements(), toRead);
+    NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
     if (deltaSize > 0) {
-      read = PR_Read(fileFd, mNewDeltas.Elements(), deltaSize*sizeof(PRUint16));
-      NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+      toRead = deltaSize*sizeof(PRUint16);
+      read = PR_Read(fileFd, mNewDeltas.Elements(), toRead);
+      NS_ENSURE_TRUE(read == toRead, NS_ERROR_FILE_CORRUPTED);
     }
 
     MutexAutoLock lock(mPrefixSetLock);
 
     mIndexPrefixes.SwapElements(mNewIndexPrefixes);
     mIndexStarts.SwapElements(mNewIndexStarts);
     mDeltas.SwapElements(mNewDeltas);
 
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
@@ -1,10 +1,15 @@
 <!ENTITY addons.windowTitle                   "Add-ons Manager">
+
 <!ENTITY search.placeholder                   "Search all add-ons">
+<!-- LOCALIZATION NOTE (search.commandKey):
+     The search command key should match findOnCmd.commandkey from browser.dtd -->
+<!ENTITY search.commandkey                    "f">
+
 <!ENTITY loading.label                        "Loading…">
 <!ENTITY listEmpty.installed.label            "You don't have any add-ons of this type installed">
 <!ENTITY listEmpty.availableUpdates.label     "No updates found">
 <!ENTITY listEmpty.recentUpdates.label        "You haven't recently updated any add-ons">
 <!ENTITY listEmpty.findUpdates.label          "Check For Updates">
 <!ENTITY listEmpty.search.label               "Could not find any matching add-ons">
 <!ENTITY listEmpty.button.label               "Learn more about add-ons">
 <!ENTITY installAddonFromFile.label           "Install Add-on From File…">
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Dave Townsend <dtownsend@oxymoronical.com>
+#   Blair McBride <bmcbride@mozilla.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
@@ -45,18 +46,21 @@ const Cr = Components.results;
 
 const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
 const PREF_EM_UPDATE_ENABLED          = "extensions.update.enabled";
 const PREF_EM_LAST_APP_VERSION        = "extensions.lastAppVersion";
 const PREF_EM_LAST_PLATFORM_VERSION   = "extensions.lastPlatformVersion";
 const PREF_EM_AUTOUPDATE_DEFAULT      = "extensions.update.autoUpdateDefault";
 const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
 
+// Note: This has to be kept in sync with the same constant in AddonRepository.jsm
 const STRICT_COMPATIBILITY_DEFAULT    = true;
 
+const TOOLKIT_ID                      = "toolkit@mozilla.org";
+
 const VALID_TYPES_REGEXP = /^[\w\-]+$/;
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 var EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ];
 
 const CATEGORY_PROVIDER_MODULE = "addon-provider-module";
 
@@ -230,16 +234,77 @@ AddonScreenshot.prototype = {
   caption: null,
 
   // Returns the screenshot URL, defaulting to the empty string
   toString: function() {
     return this.url || "";
   }
 }
 
+
+/**
+ * This represents a compatibility override for an addon.
+ *
+ * @param  aType
+ *         Overrride type - "compatible" or "incompatible"
+ * @param  aMinVersion
+ *         Minimum version of the addon to match
+ * @param  aMaxVersion
+ *         Maximum version of the addon to match
+ * @param  aAppID
+ *         Application ID used to match appMinVersion and appMaxVersion
+ * @param  aAppMinVersion
+ *         Minimum version of the application to match
+ * @param  aAppMaxVersion
+ *         Maximum version of the application to match
+ */
+function AddonCompatibilityOverride(aType, aMinVersion, aMaxVersion, aAppID,
+                                    aAppMinVersion, aAppMaxVersion) {
+  this.type = aType;
+  this.minVersion = aMinVersion;
+  this.maxVersion = aMaxVersion;
+  this.appID = aAppID;
+  this.appMinVersion = aAppMinVersion;
+  this.appMaxVersion = aAppMaxVersion;
+}
+
+AddonCompatibilityOverride.prototype = {
+  /**
+   * Type of override - "incompatible" or "compatible".
+   * Only "incompatible" is supported for now.
+   */
+  type: null,
+
+  /**
+   * Min version of the addon to match.
+   */
+  minVersion: null,
+
+  /**
+   * Max version of the addon to match.
+   */
+  maxVersion: null,
+
+  /**
+   * Application ID to match.
+   */
+  appID: null,
+
+  /**
+   * Min version of the application to match.
+   */
+  appMinVersion: null,
+
+  /**
+   * Max version of the application to match.
+   */
+  appMaxVersion: null
+};
+
+
 /**
  * A type of add-on, used by the UI to determine how to display different types
  * of add-ons.
  *
  * @param  aId
  *         The add-on type ID
  * @param  aLocaleURI
  *         The URI of a localized properties file to get the displayable name
@@ -547,53 +612,61 @@ var AddonManagerInternal = {
    * Performs a background update check by starting an update for all add-ons
    * that can be updated.
    */
   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
     if (!Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED))
       return;
 
     Services.obs.notifyObservers(null, "addons-background-update-start", null);
-    let pendingUpdates = 1;
+    let pendingUpdates = 0;
 
     function notifyComplete() {
-      if (--pendingUpdates == 0)
-        Services.obs.notifyObservers(null, "addons-background-update-complete", null);
+      if (--pendingUpdates == 0) {
+        Services.obs.notifyObservers(null,
+                                     "addons-background-update-complete",
+                                     null);
+      }
     }
 
     let scope = {};
     Components.utils.import("resource://gre/modules/AddonRepository.jsm", scope);
     Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
     scope.LightweightThemeManager.updateCurrentTheme();
 
+    pendingUpdates++;
     this.getAllAddons(function getAddonsCallback(aAddons) {
-      pendingUpdates++;
+      // Repopulate repository cache first, to ensure compatibility overrides
+      // are up to date before checking for addon updates.
       var ids = [a.id for each (a in aAddons)];
-      scope.AddonRepository.repopulateCache(ids, notifyComplete);
+      scope.AddonRepository.repopulateCache(ids, function BUC_repopulateCacheCallback() {
+        AddonManagerInternal.updateAddonRepositoryData(function BUC_updateAddonCallback() {
 
-      pendingUpdates += aAddons.length;
+          pendingUpdates += aAddons.length;
 
-      aAddons.forEach(function BUC_forEachCallback(aAddon) {
-        // Check all add-ons for updates so that any compatibility updates will
-        // be applied
-        aAddon.findUpdates({
-          onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
-            // Start installing updates when the add-on can be updated and
-            // background updates should be applied.
-            if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
-                AddonManager.shouldAutoUpdate(aAddon)) {
-              aInstall.install();
-            }
-          },
-
-          onUpdateFinished: notifyComplete
-        }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+          aAddons.forEach(function BUC_forEachCallback(aAddon) {
+            // Check all add-ons for updates so that any compatibility updates will
+            // be applied
+            aAddon.findUpdates({
+              onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
+                // Start installing updates when the add-on can be updated and
+                // background updates should be applied.
+                if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
+                    AddonManager.shouldAutoUpdate(aAddon)) {
+                  aInstall.install();
+                }
+              },
+    
+              onUpdateFinished: notifyComplete
+            }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+          });
+    
+          notifyComplete();
+        });
       });
-
-      notifyComplete();
     });
   },
 
   /**
    * Adds a add-on to the list of detected changes for this startup. If
    * addStartupChange is called multiple times for the same add-on in the same
    * startup then only the most recent change will be remembered.
    *
@@ -710,17 +783,41 @@ var AddonManagerInternal = {
    * their add-ons in response to an application change such as a blocklist
    * update.
    */
   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
     this.providers.forEach(function(provider) {
       callProvider(provider, "updateAddonAppDisabledStates");
     });
   },
+  
+  /**
+   * Notifies all providers that the repository has updated its data for
+   * installed add-ons.
+   *
+   * @param  aCallback
+   *         Function to call when operation is complete.
+   */
+  updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
+    if (!aCallback)
+      throw Components.Exception("Must specify aCallback",
+                                 Cr.NS_ERROR_INVALID_ARG);
 
+    new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", {
+      nextObject: function(aCaller, aProvider) {
+        callProvider(aProvider,
+                     "updateAddonRepositoryData",
+                     null,
+                     aCaller.callNext.bind(aCaller));
+      },
+      noMoreObjects: function(aCaller) {
+        safeCall(aCallback);
+      }
+    });
+  },
   /**
    * Asynchronously gets an AddonInstall for a URL.
    *
    * @param  aUrl
    *         The url the add-on is located at
    * @param  aCallback
    *         A callback to pass the AddonInstall to
    * @param  aMimetype
@@ -1220,16 +1317,18 @@ var AddonManagerPrivate = {
   callAddonListeners: function AMP_callAddonListeners(aMethod) {
     AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, arguments);
   },
 
   AddonAuthor: AddonAuthor,
 
   AddonScreenshot: AddonScreenshot,
 
+  AddonCompatibilityOverride: AddonCompatibilityOverride,
+
   AddonType: AddonType
 };
 
 /**
  * This is the public API that UI and developers should be calling. All methods
  * just forward to AddonManagerInternal.
  */
 var AddonManager = {
--- a/toolkit/mozapps/extensions/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/AddonRepository.jsm
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is mozilla.org
 # Portions created by the Initial Developer are Copyright (C) 2008
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Dave Townsend <dtownsend@oxymoronical.com>
 #   Ben Parr <bparr@bparr.com>
+#   Blair McBride <bmcbride@mozilla.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
@@ -69,24 +70,30 @@ XPCOMUtils.defineLazyGetter(this, "PREF_
 #ifdef MOZ_COMPATIBILITY_NIGHTLY
   return PREF_CHECK_COMPATIBILITY_BASE + ".nightly";
 #else
   return PREF_CHECK_COMPATIBILITY_BASE + "." +
          Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
 #endif
 });
 
+const PREF_EM_STRICT_COMPATIBILITY       = "extensions.strictCompatibility";
+// Note: This has to be kept in sync with the same constant in AddonManager.jsm
+const STRICT_COMPATIBILITY_DEFAULT       = true;
+
 const XMLURI_PARSE_ERROR  = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
 
 const API_VERSION = "1.5";
 const DEFAULT_CACHE_TYPES = "extension,theme,locale,dictionary";
 
 const KEY_PROFILEDIR = "ProfD";
 const FILE_DATABASE  = "addons.sqlite";
-const DB_SCHEMA      = 2;
+const DB_SCHEMA      = 3;
+
+const TOOLKIT_ID     = "toolkit@mozilla.org";
 
 ["LOG", "WARN", "ERROR"].forEach(function(aName) {
   this.__defineGetter__(aName, function() {
     Components.utils.import("resource://gre/modules/AddonLogging.jsm");
 
     LogManager.getLogger("addons.repository", this);
     return this[aName];
   });
@@ -352,16 +359,22 @@ AddonSearchResult.prototype = {
 
   /**
    * True or false depending on whether the add-on is compatible with the
    * current platform
    */
   isPlatformCompatible: true,
 
   /**
+   * Array of AddonCompatibilityOverride objects, that describe overrides for
+   * compatibility with an application versions.
+   **/
+  compatibilityOverrides: null,
+
+  /**
    * True if the add-on has a secure means of updating
    */
   providesUpdatesSecurely: true,
 
   /**
    * The current blocklist state of the add-on
    */
   blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
@@ -604,18 +617,18 @@ var AddonRepository = {
       AddonDatabase.delete(aCallback);
       return;
     }
 
     let self = this;
     getAddonsToCache(aIds, function(aAddons) {
       // Completely remove cache if there are no add-ons to cache
       if (aAddons.length == 0) {
-        this._addons = null;
-        this._pendingCallbacks = null;
+        self._addons = null;
+        self._pendingCallbacks = null;
         AddonDatabase.delete(aCallback);
         return;
       }
 
       self.getAddonsByIDs(aAddons, {
         searchSucceeded: function(aAddons) {
           self._addons = {};
           aAddons.forEach(function(aAddon) { self._addons[aAddon.id] = aAddon; });
@@ -739,35 +752,53 @@ var AddonRepository = {
     let params = {
       API_VERSION : API_VERSION,
       IDS : ids.map(encodeURIComponent).join(',')
     };
 
     let url = this._formatURLPref(PREF_GETADDONS_BYIDS, params);
 
     let self = this;
-    function handleResults(aElements, aTotalResults) {
+    function handleResults(aElements, aTotalResults, aCompatData) {
       // Don't use this._parseAddons() so that, for example,
       // incompatible add-ons are not filtered out
       let results = [];
       for (let i = 0; i < aElements.length && results.length < self._maxResults; i++) {
-        let result = self._parseAddon(aElements[i]);
+        let result = self._parseAddon(aElements[i], null, aCompatData);
         if (result == null)
           continue;
 
         // Ignore add-on if it wasn't actually requested
         let idIndex = ids.indexOf(result.addon.id);
         if (idIndex == -1)
           continue;
 
         results.push(result);
         // Ignore this add-on from now on
         ids.splice(idIndex, 1);
       }
 
+      // Include any compatibility overrides for addons not hosted by the
+      // remote repository.
+      for each (let addonCompat in aCompatData) {
+        if (addonCompat.hosted)
+          continue;
+
+        let addon = new AddonSearchResult(addonCompat.id);
+        // Compatibility overrides can only be for extensions.
+        addon.type = "extension";
+        addon.compatibilityOverrides = addonCompat.compatRanges;
+        let result = {
+          addon: addon,
+          xpiURL: null,
+          xpiHash: null
+        };
+        results.push(result);
+      }
+
       // aTotalResults irrelevant
       self._reportSuccess(results, -1);
     }
 
     this._beginSearch(url, ids.length, aCallback, handleResults);
   },
 
   /**
@@ -860,54 +891,76 @@ var AddonRepository = {
   },
 
   // Get descendant by unique tag name. Returns null if not unique tag name.
   _getUniqueDescendant: function(aElement, aTagName) {
     let elementsList = aElement.getElementsByTagName(aTagName);
     return (elementsList.length == 1) ? elementsList[0] : null;
   },
 
+  // Get direct descendant by unique tag name.
+  // Returns null if not unique tag name.
+  _getUniqueDirectDescendant: function(aElement, aTagName) {
+    let elementsList = Array.filter(aElement.children,
+                                    function(aChild) aChild.tagName == aTagName);
+    return (elementsList.length == 1) ? elementsList[0] : null;
+  },
+
   // Parse out trimmed text content. Returns null if text content empty.
   _getTextContent: function(aElement) {
     let textContent = aElement.textContent.trim();
     return (textContent.length > 0) ? textContent : null;
   },
 
   // Parse out trimmed text content of a descendant with the specified tag name
   // Returns null if the parsing unsuccessful.
   _getDescendantTextContent: function(aElement, aTagName) {
     let descendant = this._getUniqueDescendant(aElement, aTagName);
     return (descendant != null) ? this._getTextContent(descendant) : null;
   },
 
+  // Parse out trimmed text content of a direct descendant with the specified
+  // tag name.
+  // Returns null if the parsing unsuccessful.
+  _getDirectDescendantTextContent: function(aElement, aTagName) {
+    let descendant = this._getUniqueDirectDescendant(aElement, aTagName);
+    return (descendant != null) ? this._getTextContent(descendant) : null;
+  },
+
   /*
    * Creates an AddonSearchResult by parsing an <addon> element
    *
    * @param  aElement
    *         The <addon> element to parse
    * @param  aSkip
    *         Object containing ids and sourceURIs of add-ons to skip.
+   * @param  aCompatData
+   *         Array of parsed addon_compatibility elements to accosiate with the
+   *         resulting AddonSearchResult. Optional.
    * @return Result object containing the parsed AddonSearchResult, xpiURL and
    *         xpiHash if the parsing was successful. Otherwise returns null.
    */
-  _parseAddon: function(aElement, aSkip) {
+  _parseAddon: function(aElement, aSkip, aCompatData) {
     let skipIDs = (aSkip && aSkip.ids) ? aSkip.ids : [];
     let skipSourceURIs = (aSkip && aSkip.sourceURIs) ? aSkip.sourceURIs : [];
 
     let guid = this._getDescendantTextContent(aElement, "guid");
     if (guid == null || skipIDs.indexOf(guid) != -1)
       return null;
 
     let addon = new AddonSearchResult(guid);
     let result = {
       addon: addon,
       xpiURL: null,
       xpiHash: null
     };
 
+    if (aCompatData && guid in aCompatData)
+      addon.compatibilityOverrides = aCompatData[guid].compatRanges;
+
     let self = this;
     for (let node = aElement.firstChild; node; node = node.nextSibling) {
       if (!(node instanceof Ci.nsIDOMElement))
         continue;
 
       let localName = node.localName;
 
       // Handle case where the wanted string value is located in text content
@@ -1090,16 +1143,21 @@ var AddonRepository = {
     let self = this;
     let results = [];
 
     let checkCompatibility = true;
     try {
       checkCompatibility = Services.prefs.getBoolPref(PREF_CHECK_COMPATIBILITY);
     } catch(e) { }
 
+    let strictCompatibility = STRICT_COMPATIBILITY_DEFAULT;
+    try {
+      strictCompatibility = Services.prefs.getBoolPref(PREF_EM_STRICT_COMPATIBILITY);
+    } catch (e) {}
+
     function isSameApplication(aAppNode) {
       return self._getTextContent(aAppNode) == Services.appinfo.ID;
     }
 
     for (let i = 0; i < aElements.length && results.length < this._maxResults; i++) {
       let element = aElements[i];
 
       let tags = this._getUniqueDescendant(element, "compatible_applications");
@@ -1114,29 +1172,32 @@ var AddonRepository = {
         let parent = aAppNode.parentNode;
         let minVersion = self._getDescendantTextContent(parent, "min_version");
         let maxVersion = self._getDescendantTextContent(parent, "max_version");
         if (minVersion == null || maxVersion == null)
           return false;
 
         let currentVersion = Services.appinfo.version;
         return (Services.vc.compare(minVersion, currentVersion) <= 0 &&
-                Services.vc.compare(currentVersion, maxVersion) <= 0);
+                ((!strictCompatibility) ||
+                 Services.vc.compare(currentVersion, maxVersion) <= 0));
       });
 
       // Ignore add-ons not compatible with this Application
       if (!compatible) {
         if (checkCompatibility)
           continue;
 
         if (!Array.some(applications, isSameApplication))
           continue;
       }
 
-      // Add-on meets all requirements, so parse out data
+      // Add-on meets all requirements, so parse out data.
+      // Don't pass in compatiblity override data, because that's only returned
+      // in GUID searches, which don't use _parseAddons().
       let result = this._parseAddon(element, aSkip);
       if (result == null)
         continue;
 
       // Ignore add-on missing a required attribute
       let requiredAttributes = ["id", "name", "version", "type", "creator"];
       if (requiredAttributes.some(function(aAttribute) !result.addon[aAttribute]))
         continue;
@@ -1181,16 +1242,93 @@ var AddonRepository = {
                                       addon.name, addon.iconURL, addon.version);
       }
       else {
         callback(null);
       }
     });
   },
 
+  // Parses addon_compatibility nodes, that describe compatibility overrides.
+  _parseAddonCompatElement: function(aResultObj, aElement) {
+    let guid = this._getDescendantTextContent(aElement, "guid");
+    if (!guid)
+      return;
+
+    let compat = {id: guid};
+    compat.hosted = aElement.getAttribute("hosted") != "false";
+
+    function findMatchingAppRange(aNodes) {
+      let toolkitAppRange = null;
+      for (let i = 0; i < aNodes.length; i++) {
+        let node = aNodes[i];
+        let appID = this._getDescendantTextContent(node, "appID");
+        if (appID != Services.appinfo.ID && appID != TOOLKIT_ID)
+          continue;
+
+        let minVersion = this._getDescendantTextContent(node, "min_version");
+        let maxVersion = this._getDescendantTextContent(node, "max_version");
+        if (minVersion == null || maxVersion == null)
+          continue;
+
+        let appRange = { appID: appID,
+                         appMinVersion: minVersion,
+                         appMaxVersion: maxVersion };
+
+        // Only use Toolkit app ranges if no ranges match the application ID.
+        if (appID == TOOLKIT_ID)
+          toolkitAppRange = appRange;
+        else
+          return appRange;
+      }
+      return toolkitAppRange;
+    }
+
+    function parseRangeNode(aNode) {
+      let type = aNode.getAttribute("type");
+      // Only "incompatible" (blacklisting) is supported for now.
+      if (type != "incompatible")
+        return null;
+
+      let override = new AddonManagerPrivate.AddonCompatibilityOverride(type);
+
+      override.minVersion = this._getDirectDescendantTextContent(aNode, "min_version");
+      override.maxVersion = this._getDirectDescendantTextContent(aNode, "max_version");
+
+      if (!override.minVersion || !override.maxVersion)
+        return null;
+
+      let appRanges = aNode.querySelectorAll("compatible_applications > application");
+      let appRange = findMatchingAppRange.bind(this)(appRanges);
+      if (!appRange)
+        return null;
+
+      override.appID = appRange.appID;
+      override.appMinVersion = appRange.appMinVersion;
+      override.appMaxVersion = appRange.appMaxVersion;
+
+      return override;
+    }
+
+    let rangeNodes = aElement.querySelectorAll("version_ranges > version_range");
+    compat.compatRanges = Array.map(rangeNodes, parseRangeNode.bind(this))
+                               .filter(function(aItem) !!aItem);
+    if (compat.compatRanges.length == 0)
+      return;
+
+    aResultObj[compat.id] = compat;
+  },
+
+  // Parses addon_compatibility elements.
+  _parseAddonCompatData: function(aElements) {
+    let compatData = {};
+    Array.forEach(aElements, this._parseAddonCompatElement.bind(this, compatData));
+    return compatData;
+  },
+
   // Begins a new search if one isn't currently executing
   _beginSearch: function(aURI, aMaxResults, aCallback, aHandleResults) {
     if (this._searching || aURI == null || aMaxResults <= 0) {
       aCallback.searchFailed();
       return;
     }
 
     this._searching = true;
@@ -1222,17 +1360,20 @@ var AddonRepository = {
       let documentElement = responseXML.documentElement;
       let elements = documentElement.getElementsByTagName("addon");
       let totalResults = elements.length;
       let parsedTotalResults = parseInt(documentElement.getAttribute("total_results"));
       // Parsed value of total results only makes sense if >= elements.length
       if (parsedTotalResults >= totalResults)
         totalResults = parsedTotalResults;
 
-      aHandleResults(elements, totalResults);
+      let compatElements = documentElement.getElementsByTagName("addon_compatibility");
+      let compatData = self._parseAddonCompatData(compatElements);
+
+      aHandleResults(elements, totalResults, compatData);
     }, false);
     this._request.send(null);
   },
 
   // Gets the id's of local add-ons, and the sourceURI's of local installs,
   // passing the results to aCallback
   _getLocalAddonIds: function(aCallback) {
     let self = this;
@@ -1266,17 +1407,43 @@ var AddonRepository = {
       return null;
     }
 
     url = url.replace(/%([A-Z_]+)%/g, function(aMatch, aKey) {
       return (aKey in aSubstitutions) ? aSubstitutions[aKey] : aMatch;
     });
 
     return Services.urlFormatter.formatURL(url);
+  },
+
+  // Find a AddonCompatibilityOverride that matches a given aAddonVersion and
+  // application/platform version.
+  findMatchingCompatOverride: function AR_findMatchingCompatOverride(aAddonVersion,
+                                                                     aCompatOverrides,
+                                                                     aAppVersion,
+                                                                     aPlatformVersion) {
+    for (let i = 0; i < aCompatOverrides.length; i++) {
+      let override = aCompatOverrides[i];
+
+      let appVersion = null;
+      if (override.appID == TOOLKIT_ID)
+        appVersion = aPlatformVersion || Services.appinfo.platformVersion;
+      else
+        appVersion = aAppVersion || Services.appinfo.version;
+
+      if (Services.vc.compare(override.minVersion, aAddonVersion) <= 0 &&
+          Services.vc.compare(aAddonVersion, override.maxVersion) <= 0 &&
+          Services.vc.compare(override.appMinVersion, appVersion) <= 0 &&
+          Services.vc.compare(appVersion, override.appMaxVersion) <= 0) {
+        return override;
+      }
+    }
+    return null;
   }
+
 };
 AddonRepository.initialize();
 
 var AddonDatabase = {
   // true if the database connection has been opened
   initialized: false,
   // false if there was an unrecoverable error openning the database
   databaseOk: true,
@@ -1295,16 +1462,21 @@ var AddonDatabase = {
 
     getAllDevelopers: "SELECT addon_internal_id, name, url FROM developer " +
                       "ORDER BY addon_internal_id, num",
 
     getAllScreenshots: "SELECT addon_internal_id, url, width, height, " +
                        "thumbnailURL, thumbnailWidth, thumbnailHeight, caption " +
                        "FROM screenshot ORDER BY addon_internal_id, num",
 
+    getAllCompatOverrides: "SELECT addon_internal_id, type, minVersion, " +
+                           "maxVersion, appID, appMinVersion, appMaxVersion " +
+                           "FROM compatibility_override " +
+                           "ORDER BY addon_internal_id, num",
+
     insertAddon: "INSERT INTO addon VALUES (NULL, :id, :type, :name, :version, " +
                  ":creator, :creatorURL, :description, :fullDescription, " +
                  ":developerComments, :eula, :iconURL, :homepageURL, :supportURL, " +
                  ":contributionURL, :contributionAmount, :averageRating, " +
                  ":reviewCount, :reviewURL, :totalDownloads, :weeklyDownloads, " +
                  ":dailyUsers, :sourceURI, :repositoryStatus, :size, :updateDate)",
 
     insertDeveloper:  "INSERT INTO developer VALUES (:addon_internal_id, " +
@@ -1314,16 +1486,21 @@ var AddonDatabase = {
     // could be out of order due to schema changes.
     insertScreenshot: "INSERT INTO screenshot (addon_internal_id, " +
                       "num, url, width, height, thumbnailURL, " +
                       "thumbnailWidth, thumbnailHeight, caption) " +
                       "VALUES (:addon_internal_id, " +
                       ":num, :url, :width, :height, :thumbnailURL, " +
                       ":thumbnailWidth, :thumbnailHeight, :caption)",
 
+    insertCompatibilityOverride: "INSERT INTO compatibility_override VALUES " +
+                                 "(:addon_internal_id, :num, :type, " +
+                                 ":minVersion, :maxVersion, :appID, " +
+                                 ":appMinVersion, :appMaxVersion)",
+
     emptyAddon:       "DELETE FROM addon"
   },
 
   /**
    * A helper function to log an SQL error.
    *
    * @param  aError
    *         The storage error code associated with the error
@@ -1379,39 +1556,52 @@ var AddonDatabase = {
       }
       return tryAgain();
     }
 
     this.connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
     if (dbMissing)
       this._createSchema();
 
-    switch (this.connection.schemaVersion) {
-      case 0:
-        this._createSchema();
-        break;
-      case 1:
-        try {
+    try {
+      switch (this.connection.schemaVersion) {
+        case 0:
+          LOG("Recreating database schema");
+          this._createSchema();
+          break;
+        case 1:
+          LOG("Upgrading database schema");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER");
           this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER");
-          this._createIndices();
-          this.connection.schemaVersion = DB_SCHEMA;
-        } catch (e) {
-          ERROR("Failed to create database schema", e);
-          this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
-          this.connection.rollbackTransaction();
+        case 2:
+          this.connection.createTable("compatibility_override",
+                                      "addon_internal_id INTEGER, " +
+                                      "num INTEGER, " +
+                                      "type TEXT, " +
+                                      "minVersion TEXT, " +
+                                      "maxVersion TEXT, " +
+                                      "appID TEXT, " +
+                                      "appMinVersion TEXT, " +
+                                      "appMaxVersion TEXT, " +
+                                      "PRIMARY KEY (addon_internal_id, num)");
+            this._createIndices();
+            this._createTriggers();
+            this.connection.schemaVersion = DB_SCHEMA;
+        case 3:
+          break;
+        default:
           return tryAgain();
-        }
-        break;
-      case 2:
-        break;
-      default:
-        return tryAgain();
+      }
+    } catch (e) {
+      ERROR("Failed to create database schema", e);
+      this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
+      this.connection.rollbackTransaction();
+      return tryAgain();
     }
 
     return this.connection;
   },
 
   /**
    * A lazy getter for the database connection.
    */
@@ -1590,16 +1780,48 @@ var AddonDatabase = {
 
         handleCompletion: function(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
             ERROR("Error retrieving screenshots from database. Returning empty results");
             aCallback({});
             return;
           }
 
+          getAllCompatOverrides();
+        }
+      });
+    }
+
+    function getAllCompatOverrides() {
+      self.getAsyncStatement("getAllCompatOverrides").executeAsync({
+        handleResult: function(aResults) {
+          let row = null;
+          while (row = aResults.getNextRow()) {
+            let addon_internal_id = row.getResultByName("addon_internal_id");
+            if (!(addon_internal_id in addons)) {
+              WARN("Found a compatibility override not linked to an add-on in database");
+              continue;
+            }
+
+            let addon = addons[addon_internal_id];
+            if (!addon.compatibilityOverrides)
+              addon.compatibilityOverrides = [];
+            addon.compatibilityOverrides.push(self._makeCompatOverrideFromAsyncRow(row));
+          }
+        },
+
+        handleError: self.asyncErrorLogger,
+
+        handleCompletion: function(aReason) {
+          if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+            ERROR("Error retrieving compatibility overrides from database. Returning empty results");
+            aCallback({});
+            return;
+          }
+
           let returnedAddons = {};
           for each (let addon in addons)
             returnedAddons[addon.id] = addon;
           aCallback(returnedAddons);
         }
       });
     }
 
@@ -1672,18 +1894,19 @@ var AddonDatabase = {
    * @param  aCallback
    *         The callback to call once complete
    */
   _insertAddon: function AD__insertAddon(aAddon, aCallback) {
     let self = this;
     let internal_id = null;
     this.connection.beginTransaction();
 
-    // Simultaneously insert the developers and screenshots of the add-on
-    function insertDevelopersAndScreenshots() {
+    // Simultaneously insert the developers, screenshots, and compatibility
+    // overrides of the add-on.
+    function insertAdditionalData() {
       let stmts = [];
 
       // Initialize statement and parameters for inserting an array
       function initializeArrayInsert(aStatementKey, aArray, aAddParams) {
         if (!aArray || aArray.length == 0)
           return;
 
         let stmt = self.getAsyncStatement(aStatementKey);
@@ -1691,35 +1914,39 @@ var AddonDatabase = {
         aArray.forEach(function(aElement, aIndex) {
           aAddParams(params, internal_id, aElement, aIndex);
         });
 
         stmt.bindParameters(params);
         stmts.push(stmt);
       }
 
-      // Initialize statements to insert developers and screenshots
+      // Initialize statements to insert developers, screenshots, and
+      // compatibility overrides
       initializeArrayInsert("insertDeveloper", aAddon.developers,
                             self._addDeveloperParams);
       initializeArrayInsert("insertScreenshot", aAddon.screenshots,
                             self._addScreenshotParams);
+      initializeArrayInsert("insertCompatibilityOverride",
+                            aAddon.compatibilityOverrides,
+                            self._addCompatOverrideParams);
 
       // Immediately call callback if nothing to insert
       if (stmts.length == 0) {
         self.connection.commitTransaction();
         aCallback();
         return;
       }
 
       self.connection.executeAsync(stmts, stmts.length, {
         handleResult: function() {},
         handleError: self.asyncErrorLogger,
         handleCompletion: function(aReason) {
           if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
-            ERROR("Error inserting developers and screenshots into database. Attempting to continue");
+            ERROR("Error inserting additional addon metadata into database. Attempting to continue");
             self.connection.rollbackTransaction();
           }
           else {
             self.connection.commitTransaction();
           }
 
           aCallback();
         }
@@ -1735,17 +1962,17 @@ var AddonDatabase = {
         if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
           ERROR("Error inserting add-ons into database. Attempting to continue.");
           self.connection.rollbackTransaction();
           aCallback();
           return;
         }
 
         internal_id = self.connection.lastInsertRowID;
-        insertDevelopersAndScreenshots();
+        insertAdditionalData();
       }
     });
   },
 
   /**
    * Make an asynchronous statement that will insert the specified add-on
    *
    * @param  aAddon
@@ -1822,16 +2049,45 @@ var AddonDatabase = {
     bp.bindByName("thumbnailURL", aScreenshot.thumbnailURL);
     bp.bindByName("thumbnailWidth", aScreenshot.thumbnailWidth);
     bp.bindByName("thumbnailHeight", aScreenshot.thumbnailHeight);
     bp.bindByName("caption", aScreenshot.caption);
     aParams.addParams(bp);
   },
 
   /**
+   * Add compatibility override parameters to the specified
+   * mozIStorageBindingParamsArray.
+   *
+   * @param  aParams
+   *         The mozIStorageBindingParamsArray to add the parameters to
+   * @param  aInternalID
+   *         The internal_id of the add-on that this override is for
+   * @param  aOverride
+   *         The override to make the parameters from
+   * @param  aIndex
+   *         The index of this override
+   */
+  _addCompatOverrideParams: function AD_addCompatOverrideParams(aParams,
+                                                                aInternalID,
+                                                                aOverride,
+                                                                aIndex) {
+    let bp = aParams.newBindingParams();
+    bp.bindByName("addon_internal_id", aInternalID);
+    bp.bindByName("num", aIndex);
+    bp.bindByName("type", aOverride.type);
+    bp.bindByName("minVersion", aOverride.minVersion);
+    bp.bindByName("maxVersion", aOverride.maxVersion);
+    bp.bindByName("appID", aOverride.appID);
+    bp.bindByName("appMinVersion", aOverride.appMinVersion);
+    bp.bindByName("appMaxVersion", aOverride.appMaxVersion);
+    aParams.addParams(bp);
+  },
+
+  /**
    * Make add-on from an asynchronous row
    * Note: This add-on will be lacking both developers and screenshots
    *
    * @param  aRow
    *         The asynchronous row to use
    * @return The created add-on
    */
   _makeAddonFromAsyncRow: function AD__makeAddonFromAsyncRow(aRow) {
@@ -1890,16 +2146,38 @@ var AddonDatabase = {
     let thumbnailWidth = aRow.getResultByName("thumbnailWidth");
     let thumbnailHeight = aRow.getResultByName("thumbnailHeight");
     let caption = aRow.getResultByName("caption");
     return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL,
                                                    thumbnailWidth, thumbnailHeight, caption);
   },
 
   /**
+   * Make a CompatibilityOverride from an asynchronous row
+   *
+   * @param  aRow
+   *         The asynchronous row to use
+   * @return The created CompatibilityOverride
+   */
+  _makeCompatOverrideFromAsyncRow: function AD_makeCompatOverrideFromAsyncRow(aRow) {
+    let type = aRow.getResultByName("type");
+    let minVersion = aRow.getResultByName("minVersion");
+    let maxVersion = aRow.getResultByName("maxVersion");
+    let appID = aRow.getResultByName("appID");
+    let appMinVersion = aRow.getResultByName("appMinVersion");
+    let appMaxVersion = aRow.getResultByName("appMaxVersion");
+    return new AddonManagerPrivate.AddonCompatibilityOverride(type,
+                                                              minVersion,
+                                                              maxVersion,
+                                                              appID,
+                                                              appMinVersion,
+                                                              appMaxVersion);
+  },
+
+  /**
    * Synchronously creates the schema in the database.
    */
   _createSchema: function AD__createSchema() {
     LOG("Creating database schema");
     this.connection.beginTransaction();
 
     // Any errors in here should rollback
     try {
@@ -1945,36 +2223,57 @@ var AddonDatabase = {
                                   "width INTEGER, " +
                                   "height INTEGER, " +
                                   "thumbnailURL TEXT, " +
                                   "thumbnailWidth INTEGER, " +
                                   "thumbnailHeight INTEGER, " +
                                   "caption TEXT, " +
                                   "PRIMARY KEY (addon_internal_id, num)");
 
-      this._createIndices();
+      this.connection.createTable("compatibility_override",
+                                  "addon_internal_id INTEGER, " +
+                                  "num INTEGER, " +
+                                  "type TEXT, " +
+                                  "minVersion TEXT, " +
+                                  "maxVersion TEXT, " +
+                                  "appID TEXT, " +
+                                  "appMinVersion TEXT, " +
+                                  "appMaxVersion TEXT, " +
+                                  "PRIMARY KEY (addon_internal_id, num)");
 
-      this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
-        "ON addon BEGIN " +
-        "DELETE FROM developer WHERE addon_internal_id=old.internal_id; " +
-        "DELETE FROM screenshot WHERE addon_internal_id=old.internal_id; " +
-        "END");
+      this._createIndices();
+      this._createTriggers();
 
       this.connection.schemaVersion = DB_SCHEMA;
       this.connection.commitTransaction();
     } catch (e) {
       ERROR("Failed to create database schema", e);
       this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
       this.connection.rollbackTransaction();
       throw e;
     }
   },
 
   /**
+   * Synchronously creates the triggers in the database.
+   */
+  _createTriggers: function AD__createTriggers() {
+    this.connection.executeSimpleSQL("DROP TRIGGER IF EXISTS delete_addon");
+    this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
+      "ON addon BEGIN " +
+      "DELETE FROM developer WHERE addon_internal_id=old.internal_id; " +
+      "DELETE FROM screenshot WHERE addon_internal_id=old.internal_id; " +
+      "DELETE FROM compatibility_override WHERE addon_internal_id=old.internal_id; " +
+      "END");
+  },
+
+  /**
    * Synchronously creates the indices in the database.
    */
   _createIndices: function AD__createIndices() {
       this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS developer_idx " +
                                        "ON developer (addon_internal_id)");
       this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS screenshot_idx " +
                                        "ON screenshot (addon_internal_id)");
+      this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS compatibility_override_idx " +
+                                       "ON compatibility_override (addon_internal_id)");
   }
 };
--- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Dave Townsend <dtownsend@oxymoronical.com>
+#   Blair McBride <bmcbride@mozilla.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
@@ -56,16 +57,17 @@ const PREFIX_ITEM           = "urn:mozil
 const PREFIX_EXTENSION      = "urn:mozilla:extension:";
 const PREFIX_THEME          = "urn:mozilla:theme:";
 const TOOLKIT_ID            = "toolkit@mozilla.org"
 const XMLURI_PARSE_ERROR    = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
 
 const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
 
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AddonRepository.jsm");
 // shared code for suppressing bad cert dialogs
 Components.utils.import("resource://gre/modules/CertUtils.jsm");
 
 var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
            getService(Ci.nsIRDFService);
 
 ["LOG", "WARN", "ERROR"].forEach(function(aName) {
   this.__defineGetter__(aName, function() {
@@ -388,16 +390,17 @@ function parseRDFManifest(aId, aType, aU
       }
 
       let result = {
         id: aId,
         version: version,
         updateURL: getProperty(ds, targetApp, "updateLink"),
         updateHash: getProperty(ds, targetApp, "updateHash"),
         updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
+        strictCompatibility: getProperty(ds, targetApp, "strictCompatibility") == "true",
         targetApplications: [appEntry]
       };
 
       if (result.updateURL && checkSecurity &&
           result.updateURL.substring(0, 6) != "https:" &&
           (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
         WARN("updateLink " + result.updateURL + " is not secure and is not verified" +
              " by a strong enough hash (needs to be sha1 or stronger).");
@@ -589,29 +592,49 @@ UpdateParser.prototype = {
  * Tests if an update matches a version of the application or platform
  *
  * @param  aUpdate
  *         The available update
  * @param  aAppVersion
  *         The application version to use
  * @param  aPlatformVersion
  *         The platform version to use
+ * @param  aIgnoreMaxVersion
+ *         Ignore maxVersion when testing if an update matches. Optional.
+ * @param  aIgnoreStrictCompat
+ *         Ignore strictCompatibility when testing if an update matches. Optional.
+ * @param  aCompatOverrides
+ *         AddonCompatibilityOverride objects to match against. Optional.
  * @return true if the update is compatible with the application/platform
  */
-function matchesVersions(aUpdate, aAppVersion, aPlatformVersion) {
+function matchesVersions(aUpdate, aAppVersion, aPlatformVersion,
+                         aIgnoreMaxVersion, aIgnoreStrictCompat,
+                         aCompatOverrides) {
+  if (aCompatOverrides) {
+    let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
+                                                              aCompatOverrides,
+                                                              aAppVersion,
+                                                              aPlatformVersion);
+    if (override && override.type == "incompatible")
+      return false;
+  }
+
+  if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
+    aIgnoreMaxVersion = false;
+
   let result = false;
   for (let i = 0; i < aUpdate.targetApplications.length; i++) {
     let app = aUpdate.targetApplications[i];
     if (app.id == Services.appinfo.ID) {
       return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
-             (Services.vc.compare(aAppVersion, app.maxVersion) <= 0);
+             (aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
     }
     if (app.id == TOOLKIT_ID) {
       result = (Services.vc.compare(aPlatformVersion, app.minVersion) >= 0) &&
-               (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0);
+               (aIgnoreMaxVersion || (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0));
     }
   }
   return result;
 }
 
 var AddonUpdateChecker = {
   // These must be kept in sync with AddonManager
   // The update check timed out
@@ -635,58 +658,74 @@ var AddonUpdateChecker = {
    *         The version of the add-on to get new compatibility information for
    * @param  aIgnoreCompatibility
    *         An optional parameter to get the first compatibility update that
    *         is compatible with any version of the application or toolkit
    * @param  aAppVersion
    *         The version of the application or null to use the current version
    * @param  aPlatformVersion
    *         The version of the platform or null to use the current version
+   * @param  aIgnoreMaxVersion
+   *         Ignore maxVersion when testing if an update matches. Optional.
+   * @param  aIgnoreStrictCompat
+   *         Ignore strictCompatibility when testing if an update matches. Optional.
    * @return an update object if one matches or null if not
    */
   getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion,
                                                               aIgnoreCompatibility,
                                                               aAppVersion,
-                                                              aPlatformVersion) {
+                                                              aPlatformVersion,
+                                                              aIgnoreMaxVersion,
+                                                              aIgnoreStrictCompat) {
     if (!aAppVersion)
       aAppVersion = Services.appinfo.version;
     if (!aPlatformVersion)
       aPlatformVersion = Services.appinfo.platformVersion;
 
     for (let i = 0; i < aUpdates.length; i++) {
       if (Services.vc.compare(aUpdates[i].version, aVersion) == 0) {
         if (aIgnoreCompatibility) {
           for (let j = 0; j < aUpdates[i].targetApplications.length; j++) {
             let id = aUpdates[i].targetApplications[j].id;
             if (id == Services.appinfo.ID || id == TOOLKIT_ID)
               return aUpdates[i];
           }
         }
-        else if (matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion)) {
+        else if (matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion,
+                                 aIgnoreMaxVersion, aIgnoreStrictCompat)) {
           return aUpdates[i];
         }
       }
     }
     return null;
   },
 
   /**
    * Returns the newest available update from a list of update objects.
    *
    * @param  aUpdates
    *         An array of update objects
    * @param  aAppVersion
    *         The version of the application or null to use the current version
    * @param  aPlatformVersion
    *         The version of the platform or null to use the current version
+   * @param  aIgnoreMaxVersion
+   *         When determining compatible updates, ignore maxVersion. Optional.
+   * @param  aIgnoreMaxVersion
+   *         When determining compatible updates, ignore strictCompatibility. Optional.
+   * @param  aCompatOverrides
+   *         Array of AddonCompatibilityOverride to take into account. Optional.
    * @return an update object if one matches or null if not
    */
   getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates,
                                                                     aAppVersion,
-                                                                    aPlatformVersion) {
+                                                                    aPlatformVersion,
+                                                                    aIgnoreMaxVersion,
+                                                                    aIgnoreStrictCompat,
+                                                                    aCompatOverrides) {
     if (!aAppVersion)
       aAppVersion = Services.appinfo.version;
     if (!aPlatformVersion)
       aPlatformVersion = Services.appinfo.platformVersion;
 
     let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                     getService(Ci.nsIBlocklistService);
 
@@ -694,18 +733,21 @@ var AddonUpdateChecker = {
     for (let i = 0; i < aUpdates.length; i++) {
       if (!aUpdates[i].updateURL)
         continue;
       let state = blocklist.getAddonBlocklistState(aUpdates[i].id, aUpdates[i].version,
                                                    aAppVersion, aPlatformVersion);
       if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
         continue;
       if ((newest == null || (Services.vc.compare(newest.version, aUpdates[i].version) < 0)) &&
-          matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion))
+          matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion,
+                          aIgnoreMaxVersion, aIgnoreStrictCompat,
+                          aCompatOverrides)) {
         newest = aUpdates[i];
+      }
     }
     return newest;
   },
 
   /**
    * Starts an update check.
    *
    * @param  aId
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -1169,16 +1169,23 @@ function escapeAddonURI(aAddon, aUri, aU
   // maxVersion
   let app = aAddon.matchingTargetApplication;
   if (app)
     var maxVersion = app.maxVersion;
   else
     maxVersion = "";
   uri = uri.replace(/%ITEM_MAXAPPVERSION%/g, maxVersion);
 
+  let compatMode = "normal";
+  if (!XPIProvider.checkCompatibility)
+    compatMode = "ignore";
+  else if (AddonManager.strictCompatibility)
+    compatMode = "strict";
+  uri = uri.replace(/%COMPATIBILITY_MODE%/g, compatMode);
+
   // Replace custom parameters (names of custom parameters must have at
   // least 3 characters to prevent lookups for something like %D0%C8)
   var catMan = null;
   uri = uri.replace(/%(\w{3,})%/g, function(aMatch, aParam) {
     if (!catMan) {
       catMan = Cc["@mozilla.org/categorymanager;1"].
                getService(Ci.nsICategoryManager);
     }
@@ -3342,16 +3349,51 @@ var XPIProvider = {
   updateAddonAppDisabledStates: function XPI_updateAddonAppDisabledStates() {
     let addons = XPIDatabase.getAddons();
     addons.forEach(function(aAddon) {
       this.updateAddonDisabledState(aAddon);
     }, this);
   },
 
   /**
+   * Update the repositoryAddon property for all add-ons.
+   *
+   * @param  aCallback
+   *         Function to call when operation is complete.
+   */
+  updateAddonRepositoryData: function XPI_updateAddonRepositoryData(aCallback) {
+    let self = this;
+    XPIDatabase.getVisibleAddons(null, function UARD_getVisibleAddonsCallback(aAddons) {
+      let pending = aAddons.length;
+      if (pending == 0) {
+        aCallback();
+        return;
+      }
+
+      function notifyComplete() {
+        if (--pending == 0)
+          aCallback();
+      }
+
+      aAddons.forEach(function UARD_forEachCallback(aAddon) {
+        AddonRepository.getCachedAddonByID(aAddon.id,
+                                           function UARD_getCachedAddonCallback(aRepoAddon) {
+          if (aRepoAddon) {
+            aAddon._repositoryAddon = aRepoAddon;
+            aAddon.compatibilityOverrides = aRepoAddon.compatibilityOverrides;
+            self.updateAddonDisabledState(aAddon);
+          }
+
+          notifyComplete();
+        });
+      });
+    });
+  },
+
+  /**
    * When the previously selected theme is removed this method will be called
    * to enable the default theme.
    */
   enableDefaultTheme: function XPI_enableDefaultTheme() {
     LOG("Activating default theme");
     let addon = XPIDatabase.getVisibleAddonForInternalName(this.defaultSkin);
     if (addon) {
       if (addon.userDisabled) {
@@ -4047,16 +4089,19 @@ AsyncAddonListCallback.prototype = {
   handleResult: function(aResults) {
     let row = null;
     while (row = aResults.getNextRow()) {
       this.count++;
       let self = this;
       XPIDatabase.makeAddonFromRowAsync(row, function(aAddon) {
         function completeAddon(aRepositoryAddon) {
           aAddon._repositoryAddon = aRepositoryAddon;
+          aAddon.compatibilityOverrides = aRepositoryAddon ?
+                                            aRepositoryAddon.compatibilityOverrides :
+                                            null;
           self.addons.push(aAddon);
           if (self.complete && self.addons.length == self.count)
            self.callback(self.addons);
         }
 
         if ("getCachedAddonByID" in AddonRepository)
           AddonRepository.getCachedAddonByID(aAddon.id, completeAddon);
         else
@@ -6147,24 +6192,28 @@ AddonInstall.prototype = {
    *         XPI is incorrectly signed
    */
   loadManifest: function AI_loadManifest(aCallback) {
     function addRepositoryData(aAddon) {
       // Try to load from the existing cache first
       AddonRepository.getCachedAddonByID(aAddon.id, function(aRepoAddon) {
         if (aRepoAddon) {
           aAddon._repositoryAddon = aRepoAddon;
+          aAddon.compatibilityOverrides = aRepoAddon.compatibilityOverrides;
           aCallback();
           return;
         }
 
         // It wasn't there so try to re-download it
         AddonRepository.cacheAddons([aAddon.id], function() {
           AddonRepository.getCachedAddonByID(aAddon.id, function(aRepoAddon) {
             aAddon._repositoryAddon = aRepoAddon;
+            aAddon.compatibilityOverrides = aRepoAddon ?
+                                              aRepoAddon.compatibilityOverrides :
+                                              null;
             aCallback();
           });
         });
       });
     }
 
     let zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
                     createInstance(Ci.nsIZipReader);
@@ -6965,34 +7014,50 @@ UpdateChecker.prototype = {
    * Called when AddonUpdateChecker completes the update check
    *
    * @param  updates
    *         The list of update details for the add-on
    */
   onUpdateCheckComplete: function UC_onUpdateCheckComplete(aUpdates) {
     let AUC = AddonUpdateChecker;
 
+    let ignoreMaxVersion = false;
+    let ignoreStrictCompat = false;
+    if (!XPIProvider.checkCompatibility) {
+      ignoreMaxVersion = true;
+      ignoreStrictCompat = true;
+    } else if (this.addon.type == "extension" &&
+               !AddonManager.strictCompatibility &&
+               !this.addon.strictCompatibility &&
+               !this.addon.hasBinaryComponents) {
+      ignoreMaxVersion = true;
+    }
+
     // Always apply any compatibility update for the current version
     let compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
-                                                  this.syncCompatibility);
-
+                                                  this.syncCompatibility,
+                                                  null, null,
+                                                  ignoreMaxVersion,
+                                                  ignoreStrictCompat);
     // Apply the compatibility update to the database
     if (compatUpdate)
       this.addon.applyCompatibilityUpdate(compatUpdate, this.syncCompatibility);
 
     // If the request is for an application or platform version that is
     // different to the current application or platform version then look for a
     // compatibility update for those versions.
     if ((this.appVersion &&
          Services.vc.compare(this.appVersion, Services.appinfo.version) != 0) ||
         (this.platformVersion &&
          Services.vc.compare(this.platformVersion, Services.appinfo.platformVersion) != 0)) {
       compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
                                                 false, this.appVersion,
-                                                this.platformVersion);
+                                                this.platformVersion,
+                                                ignoreMaxVersion,
+                                                ignoreStrictCompat);
     }
 
     if (compatUpdate)
       this.callListener("onCompatibilityUpdateAvailable", createWrapper(this.addon));
     else
       this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon));
 
     function sendUpdateAvailableMessages(aSelf, aInstall) {
@@ -7002,19 +7067,26 @@ UpdateChecker.prototype = {
       }
       else {
         aSelf.callListener("onNoUpdateAvailable", createWrapper(aSelf.addon));
       }
       aSelf.callListener("onUpdateFinished", createWrapper(aSelf.addon),
                          AddonManager.UPDATE_STATUS_NO_ERROR);
     }
 
+    let compatOverrides = AddonManager.strictCompatibility ?
+                            null :
+                            this.addon.compatibilityOverrides;
+
     let update = AUC.getNewestCompatibleUpdate(aUpdates,
                                                this.appVersion,
-                                               this.platformVersion);
+                                               this.platformVersion,
+                                               ignoreMaxVersion,
+                                               ignoreStrictCompat,
+                                               compatOverrides);
 
     if (update && Services.vc.compare(this.addon.version, update.version) < 0) {
       for (let i = 0; i < XPIProvider.installs.length; i++) {
         // Skip installs that don't match the available update
         if (XPIProvider.installs[i].existingAddon != this.addon ||
             XPIProvider.installs[i].version != update.version)
           continue;
 
@@ -7144,16 +7216,27 @@ AddonInternal.prototype = {
     else if (app.id == TOOLKIT_ID)
       version = aPlatformVersion
 
     // Only extensions can be compatible by default; themes and language packs
     // always use strict compatibility checking.
     if (this.type == "extension" && !AddonManager.strictCompatibility &&
         !this.strictCompatibility && !this.hasBinaryComponents) {
 
+      // The repository can specify compatibility overrides.
+      // Note: For now, only blacklisting is supported by overrides.
+      if (this._repositoryAddon &&
+          this._repositoryAddon.compatibilityOverrides) {
+        let overrides = this._repositoryAddon.compatibilityOverrides;
+        let override = AddonRepository.findMatchingCompatOverride(this.version,
+                                                                  overrides);
+        if (override && override.type == "incompatible")
+          return false;
+      }
+
       // Extremely old extensions should not be compatible by default.
       let minCompatVersion;
       if (app.id == Services.appinfo.ID)
         minCompatVersion = XPIProvider.minCompatibleAppVersion;
       else if (app.id == TOOLKIT_ID)
         minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
 
       if (minCompatVersion &&
@@ -7247,17 +7330,17 @@ AddonInternal.prototype = {
    * than that in the install manifest, like compatibility information.
    *
    * @param  aObj
    *         A JS object containing the cached metadata
    */
   importMetadata: function(aObj) {
     ["targetApplications", "userDisabled", "softDisabled", "existingAddonID",
      "sourceURI", "releaseNotesURI", "installDate", "updateDate",
-     "applyBackgroundUpdates"].forEach(function(aProp) {
+     "applyBackgroundUpdates", "compatibilityOverrides"].forEach(function(aProp) {
       if (!(aProp in aObj))
         return;
 
       this[aProp] = aObj[aProp];
     }, this);
 
     // Compatibility info may have changed so update appDisabled
     this.appDisabled = !isUsableAddon(this);
@@ -7363,17 +7446,17 @@ function AddonWrapper(aAddon) {
     }
 
     return [objValue, false];
   }
 
   ["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
    "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
    "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
-   "strictCompatibility"].forEach(function(aProp) {
+   "strictCompatibility", "compatibilityOverrides"].forEach(function(aProp) {
      this.__defineGetter__(aProp, function() aAddon[aProp]);
   }, this);
 
   ["fullDescription", "developerComments", "eula", "supportURL",
    "contributionURL", "contributionAmount", "averageRating", "reviewCount",
    "reviewURL", "totalDownloads", "weeklyDownloads", "dailyUsers",
    "repositoryStatus"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function() {
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1720,16 +1720,26 @@ var gHeader = {
     window.addEventListener("focus", function(aEvent) {
       if (aEvent.target == window)
         updateNavButtonVisibility();
     }, false);
 
     updateNavButtonVisibility();
   },
 
+  focusSearchBox: function() {
+    this._search.focus();
+  },
+
+  onKeyPress: function(aEvent) {
+    if (String.fromCharCode(aEvent.charCode) == "/") {
+      this.focusSearchBox();
+    }
+  },
+
   get shouldShowNavButtons() {
     var docshellItem = window.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebNavigation)
                              .QueryInterface(Ci.nsIDocShellTreeItem);
 
     // If there is no outer frame then make the buttons visible
     if (docshellItem.rootTreeItem == docshellItem)
       return true;
--- a/toolkit/mozapps/extensions/content/extensions.xul
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -50,17 +50,18 @@
 
 <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       xmlns:xhtml="http://www.w3.org/1999/xhtml"
       id="addons-page" title="&addons.windowTitle;"
       role="application" windowtype="Addons:Manager"
       disablefastfind="true"
       ondragenter="gDragDrop.onDragOver(event)"
       ondragover="gDragDrop.onDragOver(event)"
-      ondrop="gDragDrop.onDrop(event)">
+      ondrop="gDragDrop.onDrop(event)"
+      onkeypress="gHeader.onKeyPress(event)">
 
   <xhtml:link rel="shortcut icon"
               href="chrome://mozapps/skin/extensions/extensionGeneric-16.png"/>
 
   <script type="application/javascript"
           src="chrome://mozapps/content/extensions/extensions.js"/>
   <script type="application/javascript"
           src="chrome://global/content/contentAreaUtils.js"/>
@@ -140,16 +141,21 @@
     <command id="cmd_installItem"/>
     <command id="cmd_purchaseItem"/>
     <command id="cmd_uninstallItem"/>
     <command id="cmd_cancelUninstallItem"/>
     <command id="cmd_cancelOperation"/>
     <command id="cmd_contribute"/>
   </commandset>
 
+  <keyset>
+    <key id="focusSearch" key="&search.commandkey;" modifiers="accel"
+         oncommand="gHeader.focusSearchBox();"/>
+  </keyset>
+
   <!-- main header -->
   <hbox id="header" align="center">
     <toolbarbutton id="back-btn" class="nav-button header-button" command="cmd_back"
             tooltiptext="&cmd.back.tooltip;" hidden="true" disabled="true"/>
     <toolbarbutton id="forward-btn" class="nav-button header-button" command="cmd_forward"
             tooltiptext="&cmd.forward.tooltip;" hidden="true" disabled="true"/>
     <spacer flex="1"/>
     <hbox id="updates-container" align="center">
--- a/toolkit/mozapps/extensions/test/browser/Makefile.in
+++ b/toolkit/mozapps/extensions/test/browser/Makefile.in
@@ -52,16 +52,17 @@ include $(DEPTH)/config/autoconf.mk
   browser_bug557943.js \
   browser_bug562797.js \
   browser_bug562854.js \
   browser_bug562890.js \
   browser_bug562899.js \
   browser_bug562992.js \
   browser_bug567127.js \
   browser_bug567137.js \
+  browser_bug570760.js \
   browser_bug572561.js \
   browser_bug577990.js \
   browser_bug580298.js \
   browser_bug581076.js \
   browser_bug586574.js \
   browser_bug587970.js \
   browser_bug591465.js \
   browser_bug591663.js \
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug570760.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Bug 570760 - Make ctrl-f and / focus the search box in the add-ons manager 
+
+var gManagerWindow;
+var focusCount = 0;
+
+function test() {
+  waitForExplicitFinish();
+
+  open_manager(null, function(aWindow) {
+    gManagerWindow = aWindow;
+
+    var searchBox = gManagerWindow.document.getElementById("header-search");
+    function focusHandler() {
+      searchBox.blur();
+      focusCount++;
+    }
+    searchBox.addEventListener("focus", focusHandler);
+    f_key_test();
+    slash_key_test();
+    searchBox.removeEventListener("focus", focusHandler);
+    end_test();
+  });
+}
+
+function end_test() {
+  close_manager(gManagerWindow, finish);
+}
+
+function f_key_test() {
+  EventUtils.synthesizeKey("f", { accelKey: true }, gManagerWindow);
+  is(focusCount, 1, "Search box should have been focused due to the f key");
+}
+
+function slash_key_test() {
+  EventUtils.synthesizeKey("/", { }, gManagerWindow);
+  is(focusCount, 2, "Search box should have been focused due to the / key");
+}
--- a/toolkit/mozapps/extensions/test/browser/browser_searching.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching.js
@@ -34,16 +34,17 @@ else {
 }
 
 const COMPATIBILITY_PREF = "extensions.checkCompatibility." + version;
 
 function test() {
   requestLongerTimeout(2);
   // Turn on searching for this test
   Services.prefs.setIntPref(PREF_SEARCH_MAXRESULTS, 15);
+  Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
 
   waitForExplicitFinish();
 
   gProvider = new MockProvider();
 
   gProvider.createAddons([{
     id: "addon1@tests.mozilla.org",
     name: "PASS - f",
@@ -616,16 +617,38 @@ add_test(function() {
     var item = get_addon_item("remote6");
     is(item, null, "Addon incompatible with the product should not be visible");
 
     Services.prefs.clearUserPref(COMPATIBILITY_PREF);
     run_next_test();
   });
 });
 
+// Tests that compatible-by-default addons are shown if strict compatibility checking is disabled
+add_test(function() {
+  restart_manager(gManagerWindow, null, function(aWindow) {
+    gManagerWindow = aWindow;
+    gCategoryUtilities = new CategoryUtilities(gManagerWindow);
+
+    Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
+    search("incompatible", false, function() {
+      var item = get_addon_item("remote5");
+      is_element_visible(item, "Incompatible addon should be visible");
+      isnot(item.getAttribute("notification"), "warning", "Compatibility warning should not be shown");
+  
+      var item = get_addon_item("remote6");
+      is(item, null, "Addon incompatible with the product should not be visible");
+  
+      Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+      run_next_test();
+    });
+  });
+});
+
+
 // Tests that restarting the manager doesn't change search results
 add_test(function() {
   restart_manager(gManagerWindow, null, function(aWindow) {
     gManagerWindow = aWindow;
     gCategoryUtilities = new CategoryUtilities(gManagerWindow);
 
     // We never restore to the search pane
     is(gCategoryUtilities.selectedCategory, "discover", "View should have changed to discover");
--- a/toolkit/mozapps/extensions/test/browser/browser_select_update.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_update.js
@@ -117,18 +117,16 @@ function setupUI(aFailDownloads, aFailIn
       });
       EventUtils.synthesizeMouseAtCenter(gWin.document.getElementById("next"), {}, gWin);
     });
   }, gWin);
 }
 
 function test() {
   waitForExplicitFinish();
-  requestLongerTimeout(100);
-
   run_next_test();
 }
 
 function end_test() {
   finish();
 }
 
 // Test for working updates
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_cache.xml
@@ -110,10 +110,73 @@
       </preview>
       <preview primary="0">
         <full type="image/png">http://localhost:4444/repo/3/secondFull.png</full>
         <thumbnail type="image/png">http://localhost:4444/repo/3/secondThumbnail.png</thumbnail>
         <caption>Repo Add-on 3 - Second Caption</caption>
       </preview>
     </previews>
   </addon>
+
+  <addon_compatibility hosted="true" id="123">
+    <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+    <name>PASS</name>
+    <version_ranges>
+      <!-- Will be included -->
+      <version_range type="incompatible">
+        <min_version>0.1</min_version>
+        <max_version>0.2</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>3.0</min_version>
+            <max_version>4.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Will be included -->
+      <version_range type="incompatible">
+        <min_version>0.2</min_version>
+        <max_version>0.3</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>5.0</min_version>
+            <max_version>6.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Won't be included - invalid type attribute -->
+      <version_range type="unknown">
+        <min_version>9</min_version>
+        <max_version>10</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>10.0</min_version>
+            <max_version>11.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Won't be included - no matching appID -->
+      <version_range type="incompatible">
+        <min_version>0.2</min_version>
+        <max_version>0.3</max_version>
+        <compatible_applications>
+          <application>
+            <name>Unknown App</name>
+            <application_id>123</application_id>
+            <min_version>1.0</min_version>
+            <max_version>999.0</max_version>
+            <appID>unknown-app@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
 </searchresults>
 
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
@@ -57,16 +57,79 @@
     <reviews num="1111">http://localhost:4444/review1.html</reviews>
     <total_downloads>2222</total_downloads>
     <weekly_downloads>3333</weekly_downloads>
     <daily_users>4444</daily_users>
     <last_updated epoch="1265033045">2010-02-01T14:04:05Z</last_updated>
     <install size="5555">http://localhost:4444/addons/test_AddonRepository_2.xpi</install>
   </addon>
 
+  <addon_compatibility hosted="true" id="123">
+    <guid>test1@tests.mozilla.org</guid>
+    <name>PASS</name>
+    <version_ranges>
+      <!-- Will be included -->
+      <version_range type="incompatible">
+        <min_version>0.1</min_version>
+        <max_version>0.2</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>3.0</min_version>
+            <max_version>4.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Will be included -->
+      <version_range type="incompatible">
+        <min_version>0.2</min_version>
+        <max_version>0.3</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>5.0</min_version>
+            <max_version>6.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Won't be included - invalid type attribute -->
+      <version_range type="unknown">
+        <min_version>9</min_version>
+        <max_version>10</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <application_id>666</application_id>
+            <min_version>10.0</min_version>
+            <max_version>11.0</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <!-- Won't be included - no matching appID -->
+      <version_range type="incompatible">
+        <min_version>0.2</min_version>
+        <max_version>0.3</max_version>
+        <compatible_applications>
+          <application>
+            <name>Unknown App</name>
+            <application_id>123</application_id>
+            <min_version>1.0</min_version>
+            <max_version>999.0</max_version>
+            <appID>unknown-app@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
   <!-- Fails because guid matches previously successful result -->
   <addon>
     <name>FAIL</name>
     <type id="1">Extension</type>
     <guid>test1@tests.mozilla.org</guid>
     <version>1.2</version>
     <authors><author><name>Test Creator 2</name></author></authors>
     <status id="4">Public</status>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.rdf
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+    <em:updates>
+      <RDF:Seq>
+        <!-- app id compatible update available -->
+        <RDF:li>
+          <RDF:Description>
+            <em:version>2</em:version>
+            <em:targetApplication>
+              <RDF:Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>1</em:maxVersion>
+                <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+              </RDF:Description>
+            </em:targetApplication>
+          </RDF:Description>
+        </RDF:li>
+      </RDF:Seq>
+    </em:updates>
+  </RDF:Description>
+
+  <RDF:Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+    <em:updates>
+      <RDF:Seq>
+        <!-- app id compatible update available -->
+        <RDF:li>
+          <RDF:Description>
+            <em:version>2</em:version>
+            <em:targetApplication>
+              <RDF:Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>1</em:maxVersion>
+                <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+              </RDF:Description>
+            </em:targetApplication>
+          </RDF:Description>
+        </RDF:li>
+      </RDF:Seq>
+    </em:updates>
+  </RDF:Description>
+
+</RDF:RDF>
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_compatoverrides.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9">
+  <addon>
+    <name>Test addon 2</name>
+    <type id="1">Extension</type>
+    <guid>addon2@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+
+  <addon>
+    <name>Test addon 3</name>
+    <type id="1">Extension</type>
+    <guid>addon3@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 3</name>
+    <guid>addon3@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.9</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+  <addon>
+    <name>Test addon 4</name>
+    <type id="1">Extension</type>
+    <guid>addon4@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 4</name>
+    <guid>addon4@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.9</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+  <addon>
+    <name>Test addon 5</name>
+    <type id="1">Extension</type>
+    <guid>addon5@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 5</name>
+    <guid>addon5@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.9</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>Unknown App</name>
+            <appID>unknown-app@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+  <addon>
+    <name>Test addon 6</name>
+    <type id="1">Extension</type>
+    <guid>addon6@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 6</name>
+    <guid>addon6@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.5</min_version>
+        <max_version>0.9</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+  
+  <addon>
+    <name>Test addon 7</name>
+    <type id="1">Extension</type>
+    <guid>addon7@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 7</name>
+    <guid>addon7@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.5</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>0.1</min_version>
+            <max_version>0.9</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+  
+  <addon>
+    <name>Test addon 8</name>
+    <type id="1">Extension</type>
+    <guid>addon8@tests.mozilla.org</guid>
+    <version>1.0</version>
+  </addon>
+  <addon_compatibility hosted="true">
+    <name>Test addon 8</name>
+    <guid>addon8@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>6</min_version>
+        <max_version>6.2</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>0.9</min_version>
+            <max_version>9</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <version_range type="incompatible">
+        <min_version>0.5</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>0.1</min_version>
+            <max_version>9</max_version>
+          </application>
+          <application>
+            <name>Unknown app</name>
+            <appID>unknown-app@tests.mozilla.org</appID>
+            <min_version>0.1</min_version>
+            <max_version>9</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+      <version_range type="incompatible">
+        <min_version>0.1</min_version>
+        <max_version>0.2</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>0.1</min_version>
+            <max_version>0.9</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+  <addon_compatibility hosted="false">
+    <name>Test addon 9</name>
+    <guid>addon9@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>0.5</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+  <addon_compatibility hosted="false">
+    <name>Test addon 10</name>
+    <guid>addon10@tests.mozilla.org</guid>
+    <version_ranges>
+      <version_range type="compatible">
+        <min_version>0.5</min_version>
+        <max_version>1.0</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <appID>xpcshell@tests.mozilla.org</appID>
+            <min_version>1</min_version>
+            <max_version>2</max_version>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+
+</searchresults>
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
@@ -139,9 +139,112 @@
                 <em:updateLink>http://localhost:4444/addons/test_update8.xpi</em:updateLink>
               </Description>
             </em:targetApplication>
           </Description>
         </li>
       </Seq>
     </em:updates>
   </Description>
+
+  <Description about="urn:mozilla:extension:addon9@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>1</em:maxVersion>
+                <em:updateLink>http://localhost:4444/addons/test_update9_2.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Incompatible when strict compatibility is enabled -->
+        <li>
+          <Description>
+            <em:version>3.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.9</em:minVersion>
+                <em:maxVersion>0.9</em:maxVersion>
+                <em:updateLink>http://localhost:4444/addons/test_update9_3.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Incompatible due to compatibility override -->
+        <li>
+          <Description>
+            <em:version>4.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.9</em:minVersion>
+                <em:maxVersion>0.9</em:maxVersion>
+                <em:updateLink>http://localhost:4444/addons/test_update9_4.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Addon for future version of app -->
+        <li>
+          <Description>
+            <em:version>5.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>5</em:minVersion>
+                <em:maxVersion>6</em:maxVersion>
+                <em:updateLink>http://localhost:4444/addons/test_update9_5.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+
+  <Description about="urn:mozilla:extension:addon10@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>1.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.4</em:maxVersion>
+                <em:updateLink>http://localhost:4444/addons/test_update10.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+
+  <Description about="urn:mozilla:extension:addon11@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:strictCompatibility>true</em:strictCompatibility>
+                <em:updateLink>http://localhost:4444/addons/test_update11.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
 </RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="11">
+  <addon>
+    <name>Test Addon 9</name>
+    <type id="1">Extension</type>
+    <guid>addon9@tests.mozilla.org</guid>
+  </addon>
+  <addon_compatibility hosted="true">
+    <guid>addon9@tests.mozilla.org</guid>
+    <name>Test Addon 9</name>
+    <version_ranges>
+      <version_range type="incompatible">
+        <min_version>4</min_version>
+        <max_version>4</max_version>
+        <compatible_applications>
+          <application>
+            <name>XPCShell</name>
+            <min_version>1</min_version>
+            <max_version>1</max_version>
+            <appID>xpcshell@tests.mozilla.org</appID>
+          </application>
+        </compatible_applications>
+      </version_range>
+    </version_ranges>
+  </addon_compatibility>
+</searchresults>
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
@@ -291,9 +291,129 @@
   </RDF:Description>
 
   <!-- There should be no information present for test_bug378216_14 -->
 
   <!-- Invalid update RDF -->
   <RDF:Description about="urn:mozilla:extension:test_bug378216_15@tests.mozilla.org">
     <em:updates>Foo</em:updates>
   </RDF:Description>
+
+  <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+  <Description about="urn:mozilla:extension:ignore-compat@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>1.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.5</em:minVersion>
+                <em:maxVersion>0.6</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Update for future app versions - should never be compatible -->
+        <li>
+          <Description>
+            <em:version>3.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>2</em:minVersion>
+                <em:maxVersion>3</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+
+  <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+  <Description about="urn:mozilla:extension:compat-override@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <!-- Has compatibility override, but it doesn't match this app version -->
+        <li>
+          <Description>
+            <em:version>1.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Has compatibility override, so is incompaible -->
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.5</em:minVersion>
+                <em:maxVersion>0.6</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+        <!-- Update for future app versions - should never be compatible -->
+        <li>
+          <Description>
+            <em:version>3.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>2</em:minVersion>
+                <em:maxVersion>3</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+
+  <!-- Opt-in to strict compatibility checking -->
+  <Description about="urn:mozilla:extension:compat-strict-optin@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>1.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:strictCompatibility>true</em:strictCompatibility>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
 </RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:extension:compatmode-ignore@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>2</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:extension:compatmode-normal@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>2</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:extension:compatmode-strict@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>2</em:maxVersion>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
+</RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -239,16 +239,22 @@ function do_check_addon(aActualAddon, aE
       case "sourceURI":
         do_check_eq(actualValue.spec, expectedValue);
         break;
 
       case "updateDate":
         do_check_eq(actualValue.getTime(), expectedValue.getTime());
         break;
 
+      case "compatibilityOverrides":
+        do_check_eq(actualValue.length, expectedValue.length);
+        for (let i = 0; i < actualValue.length; i++)
+          do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
+        break;
+
       default:
         if (actualValue !== expectedValue)
           do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
                    " (" + actualValue + " === " + expectedValue + ")");
     }
   });
 }
 
@@ -281,16 +287,34 @@ function do_check_screenshot(aActual, aE
   do_check_eq(aActual.height, aExpected.height);
   do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL);
   do_check_eq(aActual.thumbnailWidth, aExpected.thumbnailWidth);
   do_check_eq(aActual.thumbnailHeight, aExpected.thumbnailHeight);
   do_check_eq(aActual.caption, aExpected.caption);
 }
 
 /**
+ * Check that the actual compatibility override is the same as the expected
+ * compatibility override.
+ *
+ * @param  aAction
+ *         The actual compatibility override to check.
+ * @param  aExpected
+ *         The expected compatibility override to check against.
+ */
+function do_check_compatibilityoverride(aActual, aExpected) {
+  do_check_eq(aActual.type, aExpected.type);
+  do_check_eq(aActual.minVersion, aExpected.minVersion);
+  do_check_eq(aActual.maxVersion, aExpected.maxVersion);
+  do_check_eq(aActual.appID, aExpected.appID);
+  do_check_eq(aActual.appMinVersion, aExpected.appMinVersion);
+  do_check_eq(aActual.appMaxVersion, aExpected.appMaxVersion);
+}
+
+/**
  * Starts up the add-on manager as if it was started by the application.
  *
  * @param  aAppChanged
  *         An optional boolean parameter to simulate the case where the
  *         application has changed version since the last run. If not passed it
  *         defaults to true
  */
 function startupManager(aAppChanged) {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
@@ -31,17 +31,18 @@ const INSTALL_URL3  = "/addons/test_Addo
 // Note: name is checked separately
 var ADDON_PROPERTIES = ["id", "type", "version", "creator", "developers",
                         "description", "fullDescription", "developerComments",
                         "eula", "iconURL", "screenshots", "homepageURL",
                         "supportURL", "contributionURL", "contributionAmount",
                         "averageRating", "reviewCount", "reviewURL",
                         "totalDownloads", "weeklyDownloads", "dailyUsers",
                         "sourceURI", "repositoryStatus", "size", "updateDate",
-                        "purchaseURL", "purchaseAmount", "purchaseDisplayAmount"];
+                        "purchaseURL", "purchaseAmount", "purchaseDisplayAmount",
+                        "compatibilityOverrides"];
 
 // Results of getAddonsByIDs
 var GET_RESULTS = [{
   id:                     "test1@tests.mozilla.org",
   type:                   "extension",
   version:                "1.1",
   creator:                {
                             name: "Test Creator 1",
@@ -77,17 +78,32 @@ var GET_RESULTS = [{
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/review1.html",
   totalDownloads:         2222,
   weeklyDownloads:        3333,
   dailyUsers:             4444,
   sourceURI:              BASE_URL + INSTALL_URL2,
   repositoryStatus:       8,
   size:                   5555,
-  updateDate:             new Date(1265033045000)
+  updateDate:             new Date(1265033045000),
+  compatibilityOverrides: [{
+                            type: "incompatible",
+                            minVersion: 0.1,
+                            maxVersion: 0.2,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 3.0,
+                            appMaxVersion: 4.0
+                          }, {
+                            type: "incompatible",
+                            minVersion: 0.2,
+                            maxVersion: 0.3,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 5.0,
+                            appMaxVersion: 6.0
+                          }]
 }, {
   id:                     "test_AddonRepository_1@tests.mozilla.org",
   version:                "1.4",
   repositoryStatus:       9999
 }];
 
 // Results of retrieveRecommendedAddons and searchAddons
 var SEARCH_RESULTS = [{
@@ -171,17 +187,18 @@ var SEARCH_RESULTS = [{
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/review3.html",
   totalDownloads:         2222,
   weeklyDownloads:        3333,
   dailyUsers:             4444,
   sourceURI:              BASE_URL + "/test3.xpi",
   repositoryStatus:       8,
   size:                   5555,
-  updateDate:             new Date(1265033045000)
+  updateDate:             new Date(1265033045000),
+  
 }, {
   id:                     "purchase1@tests.mozilla.org",
   type:                   "extension",
   version:                "2.0",
   creator:                {
                             name: "Test Creator - Last Passing",
                             url:  BASE_URL + "/creatorLastPassing.html"
                           },
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
@@ -34,17 +34,18 @@ const PREF_ADDON1_CACHE_ENABLED = "exten
 const ADDON_PROPERTIES = ["id", "type", "name", "version", "creator",
                           "developers", "translators", "contributors",
                           "description", "fullDescription",
                           "developerComments", "eula", "iconURL",
                           "screenshots", "homepageURL", "supportURL",
                           "optionsURL", "aboutURL", "contributionURL",
                           "contributionAmount", "averageRating", "reviewCount",
                           "reviewURL", "totalDownloads", "weeklyDownloads",
-                          "dailyUsers", "sourceURI", "repositoryStatus"];
+                          "dailyUsers", "sourceURI", "repositoryStatus",
+                          "compatibilityOverrides"];
 
 // The size and updateDate properties are annoying to test for XPI add-ons.
 // However, since we only care about whether the repository value vs. the
 // XPI value is used, we can just test if the property value matches
 // the repository value
 const REPOSITORY_SIZE       = 9;
 const REPOSITORY_UPDATEDATE = 9;
 
@@ -85,17 +86,32 @@ const REPOSITORY_ADDONS = [{
   contributionAmount:     "$11.11",
   averageRating:          1,
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/repo/1/review.html",
   totalDownloads:         2221,
   weeklyDownloads:        3331,
   dailyUsers:             4441,
   sourceURI:              BASE_URL + "/repo/1/install.xpi",
-  repositoryStatus:       4
+  repositoryStatus:       4,
+  compatibilityOverrides: [{
+                            type: "incompatible",
+                            minVersion: 0.1,
+                            maxVersion: 0.2,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 3.0,
+                            appMaxVersion: 4.0
+                          }, {
+                            type: "incompatible",
+                            minVersion: 0.2,
+                            maxVersion: 0.3,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 5.0,
+                            appMaxVersion: 6.0
+                          }]
 }, {
   id:                     ADDON_IDS[1],
   type:                   "theme",
   name:                   "Repo Add-on 2",
   version:                "2.2",
   creator:                {
                             name: "Repo Add-on 2 - Creator",
                             url:  BASE_URL + "/repo/2/creator.html"
@@ -217,17 +233,32 @@ const WITH_CACHE = [{
   contributionAmount:     "$11.11",
   averageRating:          1,
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/repo/1/review.html",
   totalDownloads:         2221,
   weeklyDownloads:        3331,
   dailyUsers:             4441,
   sourceURI:              NetUtil.newURI(ADDON_FILES[0]).spec,
-  repositoryStatus:       4
+  repositoryStatus:       4,
+  compatibilityOverrides: [{
+                            type: "incompatible",
+                            minVersion: 0.1,
+                            maxVersion: 0.2,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 3.0,
+                            appMaxVersion: 4.0
+                          }, {
+                            type: "incompatible",
+                            minVersion: 0.2,
+                            maxVersion: 0.3,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 5.0,
+                            appMaxVersion: 6.0
+                          }]
 }, {
   id:                     ADDON_IDS[1],
   type:                   "theme",
   name:                   "XPI Add-on 2",
   version:                "1.2",
   creator:                {
                             name: "Repo Add-on 2 - Creator",
                             url:  BASE_URL + "/repo/2/creator.html"
@@ -314,17 +345,32 @@ const WITH_EXTENSION_CACHE = [{
   contributionAmount:     "$11.11",
   averageRating:          1,
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/repo/1/review.html",
   totalDownloads:         2221,
   weeklyDownloads:        3331,
   dailyUsers:             4441,
   sourceURI:              NetUtil.newURI(ADDON_FILES[0]).spec,
-  repositoryStatus:       4
+  repositoryStatus:       4,
+  compatibilityOverrides: [{
+                            type: "incompatible",
+                            minVersion: 0.1,
+                            maxVersion: 0.2,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 3.0,
+                            appMaxVersion: 4.0
+                          }, {
+                            type: "incompatible",
+                            minVersion: 0.2,
+                            maxVersion: 0.3,
+                            appID: "xpcshell@tests.mozilla.org",
+                            appMinVersion: 5.0,
+                            appMaxVersion: 6.0
+                          }]
 }, {
   id:                     ADDON_IDS[1],
   type:                   "theme",
   name:                   "XPI Add-on 2",
   version:                "1.2",
   sourceURI:              NetUtil.newURI(ADDON_FILES[1]).spec
 }, {
   id:                     ADDON_IDS[2],
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
@@ -0,0 +1,105 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that background update notifications work as expected
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+do_load_httpd_js();
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+  // Create and configure the HTTP server.
+  testserver = new nsHttpServer();
+  testserver.registerDirectory("/data/", do_get_file("data"));
+  testserver.registerDirectory("/addons/", do_get_file("addons"));
+  testserver.start(4444);
+
+  startupManager();
+
+  do_test_pending();
+  run_test_1();
+}
+
+function end_test() {
+  testserver.stop(do_test_finished);
+}
+
+// Verify that with no add-ons installed the background update notifications get
+// called
+function run_test_1() {
+  AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
+    do_check_eq(aAddons.length, 0);
+
+    Services.obs.addObserver(function() {
+      Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+      run_test_2();
+    }, "addons-background-update-complete", false);
+
+    AddonManagerPrivate.backgroundUpdateCheck();
+  });
+}
+
+// Verify that with two add-ons installed both of which claim to have updates
+// available we get the notification after both updates attempted to start
+function run_test_2() {
+  writeInstallRDFForExtension({
+    id: "addon1@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_backgroundupdate.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 1",
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "addon2@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_backgroundupdate.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon 2",
+  }, profileDir);
+
+  restartManager();
+
+  let installCount = 0;
+  let completeCount = 0;
+  let sawCompleteNotification = false;
+
+  Services.obs.addObserver(function() {
+    Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+    do_check_eq(installCount, 2);
+    sawCompleteNotification = true;
+  }, "addons-background-update-complete", false);
+
+  AddonManager.addInstallListener({
+    onNewInstall: function(aInstall) {
+      installCount++;
+    },
+
+    onDownloadFailed: function(aInstall) {
+      completeCount++;
+      if (completeCount == 2) {
+        do_check_true(sawCompleteNotification);
+        end_test();
+      }
+    }
+  });
+
+  AddonManagerPrivate.backgroundUpdateCheck();
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
@@ -0,0 +1,275 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests compatibility overrides, for when strict compatibility checking is
+// disabled. See bug 693906.
+
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_GETADDONS_BYIDS         = "extensions.getAddons.get.url";
+
+const PORT            = 4444;
+const BASE_URL        = "http://localhost:" + PORT;
+const DEFAULT_URL     = "about:blank";
+const REQ_URL         = "/data.xml";
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+                           BASE_URL + REQ_URL);
+
+do_load_httpd_js();
+var gServer;
+
+
+// Not hosted, no overrides
+var addon1 = {
+  id: "addon1@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 1",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, no overrides
+var addon2 = {
+  id: "addon2@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 2",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, matching override
+var addon3 = {
+  id: "addon3@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 3",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, matching override, wouldn't be compatible if strict chekcing is enabled
+var addon4 = {
+  id: "addon4@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 4",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "0.1",
+    maxVersion: "0.2"
+  }]
+};
+
+// Hosted, app ID doesn't match in override
+var addon5 = {
+  id: "addon5@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 5",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, addon version range doesn't match in override
+var addon6 = {
+  id: "addon6@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 6",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, app version range doesn't match in override
+var addon7 = {
+  id: "addon7@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 7",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Hosted, multiple overrides
+var addon8 = {
+  id: "addon8@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 8",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Not hosted, matching override
+var addon9 = {
+  id: "addon9@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 9",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Not hosted, override is of unsupported type (compatible)
+var addon10 = {
+  id: "addon10@tests.mozilla.org",
+  version: "1.0",
+  name: "Test addon 10",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+/*
+ * Trigger an AddonManager background update check
+ *
+ * @param  aCallback
+ *         Callback to call once the background update is complete
+ */
+function trigger_background_update(aCallback) {
+  Services.obs.addObserver({
+    observe: function(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(this, "addons-background-update-complete");
+      aCallback();
+    }
+  }, "addons-background-update-complete", false);
+
+  gInternalManager.notify(null);
+}
+
+function run_test() {
+  do_test_pending();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+  writeInstallRDFForExtension(addon1, profileDir);
+  writeInstallRDFForExtension(addon2, profileDir);
+  writeInstallRDFForExtension(addon3, profileDir);
+  writeInstallRDFForExtension(addon4, profileDir);
+  writeInstallRDFForExtension(addon5, profileDir);
+  writeInstallRDFForExtension(addon6, profileDir);
+  writeInstallRDFForExtension(addon7, profileDir);
+  writeInstallRDFForExtension(addon8, profileDir);
+  writeInstallRDFForExtension(addon9, profileDir);
+  writeInstallRDFForExtension(addon10, profileDir);
+
+  gServer = new nsHttpServer();
+  gServer.registerFile(REQ_URL, do_get_file("data/test_compatoverrides.xml"));
+  gServer.start(PORT);
+
+  startupManager();
+
+  trigger_background_update(run_test_1);
+}
+
+function end_test() {
+  gServer.stop(do_test_finished);
+}
+
+function check_compat_status(aCallback) {
+  AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+                               "addon2@tests.mozilla.org",
+                               "addon3@tests.mozilla.org",
+                               "addon4@tests.mozilla.org",
+                               "addon5@tests.mozilla.org",
+                               "addon6@tests.mozilla.org",
+                               "addon7@tests.mozilla.org",
+                               "addon8@tests.mozilla.org",
+                               "addon9@tests.mozilla.org",
+                               "addon10@tests.mozilla.org"],
+                              function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]) {
+
+    do_check_neq(a1, null);
+    do_check_eq(a1.compatibilityOverrides, null);
+    do_check_true(a1.isCompatible);
+    do_check_false(a1.appDisabled);
+
+    do_check_neq(a2, null);
+    do_check_eq(a2.compatibilityOverrides, null);
+    do_check_true(a2.isCompatible);
+    do_check_false(a2.appDisabled);
+
+    do_check_neq(a3, null);
+    do_check_neq(a3.compatibilityOverrides, null);
+    do_check_eq(a3.compatibilityOverrides.length, 1);
+    do_check_false(a3.isCompatible);
+    do_check_true(a3.appDisabled);
+
+    do_check_neq(a4, null);
+    do_check_neq(a4.compatibilityOverrides, null);
+    do_check_eq(a4.compatibilityOverrides.length, 1);
+    do_check_false(a4.isCompatible);
+    do_check_true(a4.appDisabled);
+
+    do_check_neq(a5, null);
+    do_check_eq(a5.compatibilityOverrides, null);
+    do_check_true(a5.isCompatible);
+    do_check_false(a5.appDisabled);
+
+    do_check_neq(a6, null);
+    do_check_neq(a6.compatibilityOverrides, null);
+    do_check_eq(a6.compatibilityOverrides.length, 1);
+    do_check_true(a6.isCompatible);
+    do_check_false(a6.appDisabled);
+
+    do_check_neq(a7, null);
+    do_check_neq(a7.compatibilityOverrides, null);
+    do_check_eq(a7.compatibilityOverrides.length, 1);
+    do_check_true(a7.isCompatible);
+    do_check_false(a7.appDisabled);
+
+    do_check_neq(a8, null);
+    do_check_neq(a8.compatibilityOverrides, null);
+    do_check_eq(a8.compatibilityOverrides.length, 3);
+    do_check_false(a8.isCompatible);
+    do_check_true(a8.appDisabled);
+
+    do_check_neq(a9, null);
+    do_check_neq(a9.compatibilityOverrides, null);
+    do_check_eq(a9.compatibilityOverrides.length, 1);
+    do_check_false(a9.isCompatible);
+    do_check_true(a9.appDisabled);
+
+    do_check_neq(a10, null);
+    do_check_eq(a10.compatibilityOverrides, null);
+    do_check_true(a10.isCompatible);
+    do_check_false(a10.appDisabled);
+
+    aCallback();
+  });
+}
+
+function run_test_1() {
+  check_compat_status(run_test_2);
+}
+
+function run_test_2() {
+  restartManager();
+  check_compat_status(end_test);  
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
@@ -121,18 +121,20 @@ function prepare_profile() {
     AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                  "addon2@tests.mozilla.org",
                                  "addon3@tests.mozilla.org",
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org",
                                  "addon9@tests.mozilla.org"],
                                  function([a1, a2, a3, a4, a5, a6, a9]) {
+      a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
       a2.userDisabled = true;
-      a2.applyBackgroundUpdates = false;
+      a2.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+      a3.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
       a4.userDisabled = true;
       a6.userDisabled = true;
       a9.userDisabled = false;
 
       for each (let addon in [a1, a2, a3, a4, a5, a6]) {
         oldSyncGUIDs[addon.id] = addon.syncGUID;
       }
 
@@ -204,85 +206,85 @@ function test_results() {
                                "addon9@tests.mozilla.org"],
                                function([a1, a2, a3, a4, a5, a6, a7, a8, a9]) {
     // addon1 was enabled
     do_check_neq(a1, null);
     do_check_eq(a1.syncGUID, oldSyncGUIDs[a1.id]);
     do_check_false(a1.userDisabled);
     do_check_false(a1.appDisabled);
     do_check_true(a1.isActive);
-    do_check_true(a1.applyBackgroundUpdates);
+    do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_true(a1.foreignInstall);
     do_check_false(a1.hasBinaryComponents);
     do_check_false(a1.strictCompatibility);
 
     // addon2 was disabled
     do_check_neq(a2, null);
     do_check_eq(a2.syncGUID, oldSyncGUIDs[a2.id]);
     do_check_true(a2.userDisabled);
     do_check_false(a2.appDisabled);
     do_check_false(a2.isActive);
-    do_check_false(a2.applyBackgroundUpdates);
+    do_check_eq(a2.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
     do_check_true(a2.foreignInstall);
     do_check_false(a2.hasBinaryComponents);
     do_check_false(a2.strictCompatibility);
 
     // addon3 was pending-disable in the database
     do_check_neq(a3, null);
     do_check_eq(a3.syncGUID, oldSyncGUIDs[a3.id]);
     do_check_true(a3.userDisabled);
     do_check_false(a3.appDisabled);
     do_check_false(a3.isActive);
-    do_check_true(a3.applyBackgroundUpdates);
+    do_check_eq(a3.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
     do_check_true(a3.foreignInstall);
     do_check_false(a3.hasBinaryComponents);
     do_check_false(a3.strictCompatibility);
 
     // addon4 was pending-enable in the database
     do_check_neq(a4, null);
     do_check_eq(a4.syncGUID, oldSyncGUIDs[a4.id]);
     do_check_false(a4.userDisabled);
     do_check_false(a4.appDisabled);
     do_check_true(a4.isActive);
-    do_check_true(a4.applyBackgroundUpdates);
+    do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_true(a4.foreignInstall);
     do_check_false(a4.hasBinaryComponents);
     do_check_true(a4.strictCompatibility);
 
     // addon5 was enabled in the database but needed a compatibiltiy update
     do_check_neq(a5, null);
     do_check_false(a5.userDisabled);
     do_check_false(a5.appDisabled);
     do_check_true(a5.isActive);
-    do_check_true(a5.applyBackgroundUpdates);
+    do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_true(a5.foreignInstall);
     do_check_false(a5.hasBinaryComponents);
     do_check_false(a5.strictCompatibility);
 
     // addon6 was disabled and compatible but a new version has been installed
     do_check_neq(a6, null);
     do_check_eq(a6.syncGUID, oldSyncGUIDs[a6.id]);
     do_check_eq(a6.version, "2.0");
     do_check_true(a6.userDisabled);
     do_check_false(a6.appDisabled);
     do_check_false(a6.isActive);
-    do_check_true(a6.applyBackgroundUpdates);
+    do_check_eq(a6.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_true(a6.foreignInstall);
     do_check_eq(a6.sourceURI.spec, "http://localhost:4444/addons/test_migrate4_6.xpi");
     do_check_eq(a6.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
     do_check_false(a6.hasBinaryComponents);
     do_check_false(a6.strictCompatibility);
 
     // addon7 was installed manually
     do_check_neq(a7, null);
     do_check_eq(a7.version, "1.0");
     do_check_false(a7.userDisabled);
     do_check_false(a7.appDisabled);
     do_check_true(a7.isActive);
-    do_check_true(a7.applyBackgroundUpdates);
+    do_check_eq(a7.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_false(a7.foreignInstall);
     do_check_eq(a7.sourceURI.spec, "http://localhost:4444/addons/test_migrate4_7.xpi");
     do_check_eq(a7.releaseNotesURI, null);
     do_check_false(a7.hasBinaryComponents);
     do_check_false(a7.strictCompatibility);
 
     // addon8 was enabled and has binary components
     do_check_neq(a8, null);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-const EXPECTED_SCHEMA_VERSION = 2;
+const EXPECTED_SCHEMA_VERSION = 3;
 let dbfile;
 
 function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   // Write out a minimal database.
   dbfile = gProfD.clone();
@@ -83,16 +83,34 @@ function run_test() {
       Services.obs.removeObserver(this, "addon-repository-shutdown");
       // Check the DB schema has changed once AddonRepository has freed it.
       db = AM_Cc["@mozilla.org/storage/service;1"].
            getService(AM_Ci.mozIStorageService).
            openDatabase(dbfile);
       do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
       do_check_true(db.indexExists("developer_idx"));
       do_check_true(db.indexExists("screenshot_idx"));
+      do_check_true(db.indexExists("compatibility_override_idx"));
+      do_check_true(db.tableExists("compatibility_override"));
+
+      // Check the trigger is working
+      db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
+      let internalID = db.lastInsertRowID;
+      db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
+
+      let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
+      stmt.executeStep();
+      do_check_eq(stmt.row.count, 1);
+      stmt.reset();
+
+      db.executeSimpleSQL("DELETE FROM addon");
+      stmt.executeStep();
+      do_check_eq(stmt.row.count, 0);
+      stmt.finalize();
+
       db.close();
       run_test_2();
     }
   }, "addon-repository-shutdown", null);
 
   Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
   AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) {
     do_check_neq(aAddon, null);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies that add-on update checks work
 
 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
 // This test requires lightweight themes update to be enabled even if the app
 // doesn't support lightweight themes.
 Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
 
@@ -766,19 +768,20 @@ function run_test_12() {
 
     a4.uninstall();
     restartManager();
 
     run_test_13();
   });
 }
 
-// Tests that no compatibility update is passed to the listener when there is
+// Tests that a compatibility update is passed to the listener when there is
 // compatibility info for the current version of the app but not for the
-// version of the app that the caller requested an update check for.
+// version of the app that the caller requested an update check for, when
+// strict compatibility checking is disabled.
 function run_test_13() {
   // Not initially compatible but the update check will make it compatible
   writeInstallRDFForExtension({
     id: "addon7@tests.mozilla.org",
     version: "1.0",
     updateURL: "http://localhost:4444/data/test_update.rdf",
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
@@ -793,18 +796,18 @@ function run_test_13() {
     do_check_neq(a7, null);
     do_check_true(a7.isActive);
     do_check_true(a7.isCompatible);
     do_check_false(a7.appDisabled);
     do_check_true(a7.isCompatibleWith("0"));
 
     a7.findUpdates({
       sawUpdate: false,
-      onCompatibilityUpdateAvailable: function(addon) {
-        do_throw("Should have not have seen compatibility information");
+      onNoCompatibilityUpdateAvailable: function(addon) {
+        do_throw("Should have seen compatibility information");
       },
 
       onUpdateAvailable: function(addon, install) {
         do_throw("Should not have seen an available update");
       },
 
       onUpdateFinished: function(addon) {
         do_check_true(addon.isCompatible);
@@ -1047,20 +1050,123 @@ function run_test_16() {
 
                 AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
                   do_check_neq(a2.syncGUID, null);
                   do_check_eq(oldGUID, a2.syncGUID);
 
                   a2.uninstall();
                   restartManager();
 
-                  end_test();
+                  run_test_17();
                 });
               }
             });
             aInstall.install();
           }, "application/x-xpinstall");
         });
       }
     });
     aInstall.install();
   }, "application/x-xpinstall");
 }
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_17() {
+  writeInstallRDFForExtension({
+    id: "addon9@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 9",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.addInstallListener({
+    onNewInstall: function(aInstall) {
+      if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+        do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+      do_check_eq(aInstall.version, "3.0");
+    },
+    onDownloadFailed: function(aInstall) {
+      do_execute_soon(run_test_18);
+    }
+  });
+
+  Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, "http://localhost:4444/data/test_update.xml");
+  Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+  // Fake a timer event
+  gInternalManager.notify(null);
+}
+
+// Tests that compatibility updates are applied to addons when the updated
+// compatibility data wouldn't match with strict compatibility enabled.
+function run_test_18() {
+  writeInstallRDFForExtension({
+    id: "addon10@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 10",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon10@tests.mozilla.org", function(a10) {
+    do_check_neq(a10, null);
+
+    a10.findUpdates({
+      onNoCompatibilityUpdateAvailable: function() {
+        do_throw("Should have seen compatibility information");
+      },
+
+      onUpdateAvailable: function() {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        run_test_19();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_19() {
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onUpdateAvailable: function() {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+   }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_compatmode.js
@@ -0,0 +1,198 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update check correctly fills in the
+// %COMPATIBILITY_MODE% token in the update URL.
+
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+do_load_httpd_js();
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var COMPATIBILITY_PREF;
+
+function run_test() {
+  do_test_pending();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+  
+  var channel = "default";
+  try {
+    channel = Services.prefs.getCharPref("app.update.channel");
+  } catch (e) { }
+  if (channel != "aurora" &&
+      channel != "beta" &&
+      channel != "release") {
+    var version = "nightly";
+  } else {
+    version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
+  }  
+  COMPATIBILITY_PREF = "extensions.checkCompatibility." + version;
+
+  // Create and configure the HTTP server.
+  testserver = new nsHttpServer();
+  testserver.registerDirectory("/data/", do_get_file("data"));
+  testserver.registerDirectory("/addons/", do_get_file("addons"));
+  testserver.start(4444);
+
+  writeInstallRDFForExtension({
+    id: "compatmode-normal@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon - normal"
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "compatmode-strict@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon - strict"
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "compatmode-strict-optin@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon - strict opt-in",
+    strictCompatibility: true
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "compatmode-ignore@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }],
+    name: "Test Addon - ignore",
+  }, profileDir);
+
+  startupManager();
+  run_test_1();
+}
+
+function end_test() {
+  testserver.stop(do_test_finished);
+}
+
+
+// Strict compatibility checking disabled.
+function run_test_1() {
+  do_print("Testing with strict compatibility checking disabled");
+  Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+  AddonManager.getAddonByID("compatmode-normal@tests.mozilla.org", function(addon) {
+    do_check_neq(addon, null);
+    addon.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onNoUpdateAvailable: function() {
+        do_throw("Should have seen an available update");
+      },
+
+      onUpdateAvailable: function(addon, install) {
+        do_check_eq(install.version, "2.0")
+      },
+
+      onUpdateFinished: function() {
+        run_test_2();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
+
+// Strict compatibility checking enabled.
+function run_test_2() {
+  do_print("Testing with strict compatibility checking enabled");
+  Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+  AddonManager.getAddonByID("compatmode-strict@tests.mozilla.org", function(addon) {
+    do_check_neq(addon, null);
+    addon.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onNoUpdateAvailable: function() {
+        do_throw("Should have seen an available update");
+      },
+
+      onUpdateAvailable: function(addon, install) {
+        do_check_eq(install.version, "2.0")
+      },
+
+      onUpdateFinished: function() {
+        run_test_3();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
+
+// Strict compatibility checking opt-in.
+function run_test_3() {
+  do_print("Testing with strict compatibility disabled, but addon opt-in");
+  Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+  AddonManager.getAddonByID("compatmode-strict-optin@tests.mozilla.org", function(addon) {
+    do_check_neq(addon, null);
+    addon.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onUpdateAvailable: function(addon, install) {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        run_test_4();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
+
+// Compatibility checking disabled.
+function run_test_4() {
+  do_print("Testing with all compatibility checking disabled");
+  Services.prefs.setBoolPref(COMPATIBILITY_PREF, false);
+  AddonManager.getAddonByID("compatmode-ignore@tests.mozilla.org", function(addon) {
+    do_check_neq(addon, null);
+    addon.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onNoUpdateAvailable: function() {
+        do_throw("Should have seen an available update");
+      },
+
+      onUpdateAvailable: function(addon, install) {
+        do_check_eq(install.version, "2.0")
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work correctly when compatibility
+// check is disabled.
+
+
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+do_load_httpd_js();
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+  // Create and configure the HTTP server.
+  testserver = new nsHttpServer();
+  testserver.registerDirectory("/data/", do_get_file("data"));
+  testserver.registerDirectory("/addons/", do_get_file("addons"));
+  testserver.start(4444);
+
+  run_test_1();
+}
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_1() {
+  writeInstallRDFForExtension({
+    id: "addon9@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 9",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.addInstallListener({
+    onNewInstall: function(aInstall) {
+      if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+        do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+      do_check_eq(aInstall.version, "4.0");
+    },
+    onDownloadFailed: function(aInstall) {
+      do_execute_soon(run_test_2);
+    }
+  });
+
+  Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, "http://localhost:4444/data/test_update.xml");
+  Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+  // Fake a timer event
+  gInternalManager.notify(null);
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_2() {
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onNoUpdateAvailable: function() {
+        do_throw("Should have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies that add-on update checks work
 
 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
 // This test requires lightweight themes update to be enabled even if the app
 // doesn't support lightweight themes.
 Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
 
 Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
@@ -1011,11 +1013,80 @@ function check_test_15(aInstall) {
     do_check_neq(a1, null);
     do_check_eq(a1.version, "2.0");
     a1.uninstall();
 
     do_check_eq(a8, null);
 
     restartManager();
 
-    end_test();
+    run_test_16();
   });
 }
+
+// Test that the update check correctly observes the
+// extensions.strictCompatibility pref and compatibility overrides.
+function run_test_16() {
+  writeInstallRDFForExtension({
+    id: "addon9@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 9",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.addInstallListener({
+    onNewInstall: function(aInstall) {
+      if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+        do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+      do_check_eq(aInstall.version, "2.0");
+    },
+    onDownloadFailed: function(aInstall) {
+      do_execute_soon(run_test_17);
+    }
+  });
+
+  Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, "http://localhost:4444/data/test_update.xml");
+  Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+  // Fake a timer event
+  gInternalManager.notify(null);
+}
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_17() {
+
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onUpdateAvailable: function() {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
@@ -1,16 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies that AddonUpdateChecker works correctly
 
 Components.utils.import("resource://gre/modules/AddonUpdateChecker.jsm");
 
+
+var channel = "default";
+try {
+  channel = Services.prefs.getCharPref("app.update.channel");
+} catch (e) { }
+if (channel != "aurora" && channel != "beta" && channel != "release")
+  var version = "nightly";
+else
+  version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
+const COMPATIBILITY_PREF = "extensions.checkCompatibility." + version;
+
+
 do_load_httpd_js();
 var testserver;
 
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   // Create and configure the HTTP server.
   testserver = new nsHttpServer();
@@ -221,12 +233,92 @@ function run_test_11() {
                                      "extension", null,
                                      "http://localhost:4444/data/test_updatecheck.rdf", {
     onUpdateCheckComplete: function(updates) {
       do_throw("Update check should have failed");
     },
 
     onUpdateCheckError: function(status) {
       do_check_eq(status, AddonUpdateChecker.ERROR_PARSE_ERROR);
-      end_test();
+      run_test_12();
+    }
+  });
+}
+
+function run_test_12() {
+  AddonUpdateChecker.checkForUpdates("ignore-compat@tests.mozilla.org",
+                                     "extension", null,
+                                     "http://localhost:4444/data/test_updatecheck.rdf", {
+    onUpdateCheckComplete: function(updates) {
+      do_check_eq(updates.length, 3);
+      let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+                                                                null,
+                                                                null,
+                                                                true);
+      do_check_neq(update, null);
+      do_check_eq(update.version, 2);
+      run_test_13();
+    },
+
+    onUpdateCheckError: function(status) {
+      do_throw("Update check failed with status " + status);
     }
   });
 }
+
+function run_test_13() {
+  AddonUpdateChecker.checkForUpdates("compat-override@tests.mozilla.org",
+                                     "extension", null,
+                                     "http://localhost:4444/data/test_updatecheck.rdf", {
+    onUpdateCheckComplete: function(updates) {
+      do_check_eq(updates.length, 3);
+      let overrides = [{
+        type: "incompatible",
+        minVersion: 1,
+        maxVersion: 2,
+        appID: "xpcshell@tests.mozilla.org",
+        appMinVersion: 0.1,
+        appMaxVersion: 0.2
+      }, {
+        type: "incompatible",
+        minVersion: 2,
+        maxVersion: 2,
+        appID: "xpcshell@tests.mozilla.org",
+        appMinVersion: 1,
+        appMaxVersion: 2
+      }];
+      let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+                                                                null,
+                                                                null,
+                                                                true,
+                                                                false,
+                                                                overrides);
+      do_check_neq(update, null);
+      do_check_eq(update.version, 1);
+      run_test_14();
+    },
+
+    onUpdateCheckError: function(status) {
+      do_throw("Update check failed with status " + status);
+    }
+  });
+}
+
+function run_test_14() {
+  AddonUpdateChecker.checkForUpdates("compat-strict-optin@tests.mozilla.org",
+                                     "extension", null,
+                                     "http://localhost:4444/data/test_updatecheck.rdf", {
+    onUpdateCheckComplete: function(updates) {
+      do_check_eq(updates.length, 1);
+      let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+                                                                null,
+                                                                null,
+                                                                true,
+                                                                false);
+      do_check_eq(update, null);
+      end_test();
+    },
+
+    onUpdateCheckError: function(status) {
+      do_throw("Update check failed with status " + status);
+    }
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -4,16 +4,17 @@ tail =
 
 [test_AddonRepository.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_AddonRepository_cache.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_LightweightThemeManager.js]
+[test_backgroundupdate.js]
 [test_badschema.js]
 [test_blocklistchange.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_bootstrap.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_bug299716.js]
@@ -122,16 +123,17 @@ fail-if = os == "android"
 [test_bug619730.js]
 [test_bug620837.js]
 [test_bug655254.js]
 [test_bug659772.js]
 [test_bug675371.js]
 [test_cacheflush.js]
 [test_checkcompatibility.js]
 [test_ChromeManifestParser.js]
+[test_compatoverrides.js]
 [test_corrupt.js]
 [test_corrupt_strictcompat.js]
 [test_dictionary.js]
 [test_disable.js]
 [test_distribution.js]
 [test_dss.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
@@ -193,20 +195,24 @@ fail-if = os == "android"
 [test_types.js]
 [test_uninstall.js]
 [test_update.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_update_strictcompat.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
+[test_update_ignorecompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
 [test_updatecheck.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_updateid.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
+[test_update_compatmode.js]
 [test_upgrade.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_upgrade_strictcompat.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs.js
@@ -1,13 +1,14 @@
 // ----------------------------------------------------------------------------
 // Test whether passing a simple string to InstallTrigger.install throws an
 // exception
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   var triggers = encodeURIComponent(JSON.stringify(TESTROOT + "unsigned.xpi"));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     // Allow the in-page load handler to run first
     executeSoon(page_loaded);
   }, true);
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_badargs2.js
@@ -1,13 +1,14 @@
 // ----------------------------------------------------------------------------
 // Test whether passing an undefined url InstallTrigger.install throws an
 // exception
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": {
       URL: undefined
     }
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_cancel.js
@@ -1,15 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // ----------------------------------------------------------------------------
 // Tests that cancelling multiple installs doesn't fail
 function test() {
+  ignoreAllUncaughtExceptions();
   Harness.installConfirmCallback = confirm_install;
   Harness.installEndedCallback = install_ended;
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_enabled3.js
@@ -1,11 +1,12 @@
 // ----------------------------------------------------------------------------
 // Test whether an InstallTrigger.install call fails when xpinstall is disabled
 function test() {
+  ignoreAllUncaughtExceptions();
   Harness.installDisabledCallback = install_disabled;
   Harness.installBlockedCallback = allow_blocked;
   Harness.installConfirmCallback = confirm_install;
   Harness.setup();
 
   Services.prefs.setBoolPref("xpinstall.enabled", false);
 
   var triggers = encodeURIComponent(JSON.stringify({
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_localfile2.js
@@ -1,13 +1,14 @@
 // ----------------------------------------------------------------------------
 // Test whether an install fails if the url is a local file when requested from
 // web content
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
                      .getService(Components.interfaces.nsIChromeRegistry);
   
   var chromeroot = getChromeRoot(gTestPath);              
   try {
     var xpipath = cr.convertChromeURL(makeURI(chromeroot + "unsigned.xpi")).spec;
   } catch (ex) {
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist3.js
@@ -1,12 +1,13 @@
 // ----------------------------------------------------------------------------
 // Tests installing an unsigned add-on through a navigation. Should not be
 // blocked since the referer is whitelisted.
 function test() {
+  ignoreAllUncaughtExceptions();
   Harness.installConfirmCallback = confirm_install;
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2811,16 +2811,19 @@ XRE_main(int argc, char* argv[], const n
 
     // pass some basic info from the app data
     if (appData.vendor)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"),
                                          nsDependentCString(appData.vendor));
     if (appData.name)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"),
                                          nsDependentCString(appData.name));
+    if (appData.ID)
+      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductID"),
+                                         nsDependentCString(appData.ID));
     if (appData.version)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"),
                                          nsDependentCString(appData.version));
     if (appData.buildID)
       CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"),
                                          nsDependentCString(appData.buildID));
 
     nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
--- a/tools/profiler/sps/TableTicker.cpp
+++ b/tools/profiler/sps/TableTicker.cpp
@@ -287,32 +287,32 @@ void ProfileEntry::WriteTag(Profile *pro
     }
   }
 #endif
 }
 
 #define PROFILE_DEFAULT_ENTRY 100000
 void mozilla_sampler_init()
 {
-  const char *val = PR_GetEnv("MOZ_PROFILER_SPS");
-  if (!val || !*val) {
-    return;
-  }
-
   // TODO linux port: Use TLS with ifdefs
   // TODO window port: See bug 683229 comment 15
   // profiler uses getspecific because TLS is not supported on android.
   // getspecific was picked over nspr because it had less overhead required
   // to make the checkpoint function fast.
   if (pthread_key_create(&pkey_stack, NULL) ||
         pthread_key_create(&pkey_ticker, NULL)) {
     LOG("Failed to init.");
     return;
   }
 
+  const char *val = PR_GetEnv("MOZ_PROFILER_SPS");
+  if (!val || !*val) {
+    return;
+  }
+
   TableTicker *t = new TableTicker(PROFILE_DEFAULT_ENTRY, 10);
   pthread_setspecific(pkey_ticker, t);
   pthread_setspecific(pkey_stack, t->GetStack());
 
   t->Start();
 }
 
 void mozilla_sampler_deinit()
--- a/tools/profiler/sps/sps_sampler.h
+++ b/tools/profiler/sps/sps_sampler.h
@@ -38,17 +38,16 @@
 
 #include <pthread.h>
 #include "base/atomicops.h"
 #include "nscore.h"
 
 // TODO Merge into Sampler.h
 
 extern pthread_key_t pkey_stack;
-extern pthread_key_t pkey_ticker;
 
 #define SAMPLER_INIT() mozilla_sampler_init();
 #define SAMPLER_DEINIT() mozilla_sampler_deinit();
 #define SAMPLE_CHECKPOINT(name_space, info) mozilla::SamplerStackFrameRAII only_one_sampleraii_per_scope(FULLFUNCTION, name_space "::" info);
 #define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info);
 
 // STORE_SEQUENCER: Because signals can interrupt our profile modification
 //                  we need to make stores are not re-ordered by the compiler
--- a/tools/trace-malloc/lib/nsTraceMalloc.c
+++ b/tools/trace-malloc/lib/nsTraceMalloc.c
@@ -952,17 +952,17 @@ backtrace(tm_thread *t, int skip, int *i
     stack_buffer_info *info = &t->backtrace_buf;
     void ** new_stack_buffer;
     size_t new_stack_buffer_size;
     nsresult rv;
 
     t->suppress_tracing++;
 
     if (!stacks_enabled) {
-#if defined(XP_MACOSX) && defined(__i386)
+#if defined(XP_MACOSX)
         /* Walk the stack, even if stacks_enabled is false. We do this to
            check if we must set immediate_abort. */
         info->entries = 0;
         rv = NS_StackWalk(stack_callback, skip, info);
         *immediate_abort = rv == NS_ERROR_UNEXPECTED;
         if (rv == NS_ERROR_UNEXPECTED || info->entries == 0) {
             t->suppress_tracing--;
             return NULL;
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -433,17 +433,17 @@ void nsView::DoResetWidgetBounds(bool aM
                                  bool aInvalidateChangedSize) {
   // The geometry of a root view's widget is controlled externally,
   // NOT by sizing or positioning the view
   if (mViewManager->GetRootViewImpl() == this) {
     return;
   }
   
   nsIntRect curBounds;
-  mWindow->GetBounds(curBounds);
+  mWindow->GetClientBounds(curBounds);
 
   nsWindowType type;
   mWindow->GetWindowType(type);
 
   if (curBounds.IsEmpty() && mDimBounds.IsEmpty() && type == eWindowType_popup) {
     // Don't manipulate empty popup widgets. For example there's no point
     // moving hidden comboboxes around, or doing X server roundtrips
     // to compute their true screen position. This could mean that WidgetToScreen
@@ -457,24 +457,26 @@ void nsView::DoResetWidgetBounds(bool aM
   nsIntRect newBounds = CalcWidgetBounds(type);
 
   bool changedPos = curBounds.TopLeft() != newBounds.TopLeft();
   bool changedSize = curBounds.Size() != newBounds.Size();
 
   // Child views are never attached to top level widgets, this is safe.
   if (changedPos) {
     if (changedSize && !aMoveOnly) {
-      mWindow->Resize(newBounds.x, newBounds.y, newBounds.width, newBounds.height,
-                      aInvalidateChangedSize);
+      mWindow->ResizeClient(newBounds.x, newBounds.y,
+                            newBounds.width, newBounds.height,
+                            aInvalidateChangedSize);
     } else {
-      mWindow->Move(newBounds.x, newBounds.y);
+      mWindow->MoveClient(newBounds.x, newBounds.y);
     }
   } else {
     if (changedSize && !aMoveOnly) {
-      mWindow->Resize(newBounds.width, newBounds.height, aInvalidateChangedSize);
+      mWindow->ResizeClient(newBounds.width, newBounds.height,
+                            aInvalidateChangedSize);
     } // else do nothing!
   }
 }
 
 void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget)
 {
   nsRect dims = aRect;
   dims.MoveBy(mPosX, mPosY);
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -113,18 +113,18 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #endif
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #endif
 
 #define NS_IWIDGET_IID \
-  { 0x34460b01, 0x3dc2, 0x4b58, \
-    { 0x8e, 0xd3, 0x7e, 0x7c, 0x33, 0xb5, 0x78, 0x8b } }
+  { 0x41fc0f2c, 0x65c2, 0x418e, \
+    { 0x89, 0x91, 0x5f, 0x0c, 0xa7, 0x01, 0x05, 0x34 } }
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
 #define NS_STYLE_WINDOW_SHADOW_MENU             2
@@ -668,16 +668,31 @@ class nsIWidget : public nsISupports {
      *
      * @param aX the new x position expressed in the parent's coordinate system
      * @param aY the new y position expressed in the parent's coordinate system
      *
      **/
     NS_IMETHOD Move(PRInt32 aX, PRInt32 aY) = 0;
 
     /**
+     * Reposition this widget so that the client area has the given offset.
+     *
+     * @param aX       the new x offset of the client area expressed as an
+     *                 offset from the origin of the client area of the parent
+     *                 widget (for root widgets and popup widgets it is in
+     *                 screen coordinates)
+     * @param aY       the new y offset of the client area expressed as an
+     *                 offset from the origin of the client area of the parent
+     *                 widget (for root widgets and popup widgets it is in
+     *                 screen coordinates)
+     *
+     **/
+    NS_IMETHOD MoveClient(PRInt32 aX, PRInt32 aY) = 0;
+
+    /**
      * Resize this widget. 
      *
      * @param aWidth  the new width expressed in the parent's coordinate system
      * @param aHeight the new height expressed in the parent's coordinate system
      * @param aRepaint whether the widget should be repainted
      *
      */
     NS_IMETHOD Resize(PRInt32 aWidth,
@@ -696,20 +711,39 @@ class nsIWidget : public nsISupports {
      */
     NS_IMETHOD Resize(PRInt32 aX,
                       PRInt32 aY,
                       PRInt32 aWidth,
                       PRInt32 aHeight,
                       bool     aRepaint) = 0;
 
     /**
-     * Resize and reposition the inner client area of the widget.
+     * Resize the widget so that the inner client area has the given size.
+     *
+     * @param aWidth   the new width of the client area.
+     * @param aHeight  the new height of the client area.
+     * @param aRepaint whether the widget should be repainted
      *
-     * @param aX       the new x offset expressed in the parent's coordinate system
-     * @param aY       the new y offset expressed in the parent's coordinate system
+     */
+    NS_IMETHOD ResizeClient(PRInt32 aWidth,
+                            PRInt32 aHeight,
+                            bool  aRepaint) = 0;
+
+    /**
+     * Resize and reposition the widget so tht inner client area has the given
+     * offset and size.
+     *
+     * @param aX       the new x offset of the client area expressed as an
+     *                 offset from the origin of the client area of the parent
+     *                 widget (for root widgets and popup widgets it is in
+     *                 screen coordinates)
+     * @param aY       the new y offset of the client area expressed as an
+     *                 offset from the origin of the client area of the parent
+     *                 widget (for root widgets and popup widgets it is in
+     *                 screen coordinates)
      * @param aWidth   the new width of the client area.
      * @param aHeight  the new height of the client area.
      * @param aRepaint whether the widget should be repainted
      *
      */
     NS_IMETHOD ResizeClient(PRInt32 aX,
                             PRInt32 aY,
                             PRInt32 aWidth,
@@ -794,19 +828,20 @@ class nsIWidget : public nsISupports {
      * includes any title bar on the window.
      *
      * @param aRect   On return it holds the  x, y, width and height of
      *                this widget.
      */
     NS_IMETHOD GetScreenBounds(nsIntRect &aRect) = 0;
 
     /**
-     * Get this widget's client area dimensions, if the window has a 3D
-     * border appearance this returns the area inside the border. Origin
-     * is always zero.
+     * Get this widget's client area bounds, if the window has a 3D border
+     * appearance this returns the area inside the border. The position is the
+     * position of the client area relative to the client area of the parent
+     * widget (for root widgets and popup widgets it is in screen coordinates).
      *
      * @param aRect   On return it holds the  x. y, width and height of
      *                the client area of this widget.
      */
     NS_IMETHOD GetClientBounds(nsIntRect &aRect) = 0;
 
     /**
      * Get the non-client area dimensions of the window.
--- a/widget/src/android/nsAppShell.cpp
+++ b/widget/src/android/nsAppShell.cpp
@@ -209,69 +209,85 @@ nsAppShell::ProcessNextNativeEvent(bool 
 {
     EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait);
 
     nsAutoPtr<AndroidGeckoEvent> curEvent;
     AndroidGeckoEvent *nextEvent;
     {
         MutexAutoLock lock(mCondLock);
 
-        curEvent = GetNextEvent();
+        curEvent = PopNextEvent();
         if (!curEvent && mayWait) {
             // hmm, should we really hardcode this 10s?
 #if defined(DEBUG_ANDROID_EVENTS)
             PRTime t0, t1;
             EVLOG("nsAppShell: waiting on mQueueCond");
             t0 = PR_Now();
 
             mQueueCond.Wait(PR_MillisecondsToInterval(10000));
             t1 = PR_Now();
             EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000);
 #else
             mQueueCond.Wait();
 #endif
 
-            curEvent = GetNextEvent();
+            curEvent = PopNextEvent();
         }
     }
 
     if (!curEvent)
         return false;
 
     // Combine subsequent events of the same type
 
     nextEvent = PeekNextEvent();
 
     while (nextEvent) {
         int curType = curEvent->Type();
         int nextType = nextEvent->Type();
 
-        // Do not skip draw events if the Java compositor is in use, since the Java compositor
-        // updates only the rect that changed - thus we will lose updates.
-#ifndef MOZ_JAVA_COMPOSITOR
-        while (nextType == AndroidGeckoEvent::DRAW &&
+        while (nextType == AndroidGeckoEvent::DRAW && mLastDrawEvent &&
                mNumDraws > 1)
         {
             // skip this draw, since there's a later one already in the queue.. this will let us
             // deal with sequences that look like:
             //   MOVE DRAW MOVE DRAW MOVE DRAW
             // and end up with just
             //   MOVE DRAW
             // when we process all the events.
-            RemoveNextEvent();
+
+            // Combine the next draw event's rect with the last one in the queue
+            const nsIntRect& nextRect = nextEvent->Rect();
+            const nsIntRect& lastRect = mLastDrawEvent->Rect();
+            int combinedArea = (lastRect.width * lastRect.height) +
+                               (nextRect.width * nextRect.height);
+
+            nsIntRect combinedRect = lastRect.Union(nextRect);
+            mLastDrawEvent->Init(AndroidGeckoEvent::DRAW, combinedRect);
+
+            // XXX We may want to consider using regions instead of rectangles.
+            //     Print an error if we're upload a lot more than we would
+            //     if we handled this as two separate events.
+            int boundsArea = combinedRect.width * combinedRect.height;
+            if (boundsArea > combinedArea * 8)
+                ALOG("nsAppShell::ProcessNextNativeEvent: "
+                     "Area of bounds greatly exceeds combined area: %d > %d",
+                     boundsArea, combinedArea);
+
+            // Remove the next draw event
+            PopNextEvent();
             delete nextEvent;
 
 #if defined(DEBUG_ANDROID_EVENTS)
             ALOG("# Removing DRAW event (%d outstanding)", mNumDraws);
 #endif
 
             nextEvent = PeekNextEvent();
             nextType = nextEvent->Type();
         }
-#endif
 
         // If the next type of event isn't the same as the current type,
         // we don't coalesce.
         if (nextType != curType)
             break;
 
         // Can only coalesce motion move events, for motion events
         if (curType != AndroidGeckoEvent::MOTION_EVENT)
@@ -280,18 +296,17 @@ nsAppShell::ProcessNextNativeEvent(bool 
         if (!(curEvent->Action() == AndroidMotionEvent::ACTION_MOVE &&
               nextEvent->Action() == AndroidMotionEvent::ACTION_MOVE))
             break;
 
 #if defined(DEBUG_ANDROID_EVENTS)
         ALOG("# Removing % 2d event", curType);
 #endif
 
-        RemoveNextEvent();
-        curEvent = nextEvent;
+        curEvent = PopNextEvent();
         nextEvent = PeekNextEvent();
     }
 
     EVLOG("nsAppShell: event %p %d [ndraws %d]", (void*)curEvent.get(), curEvent->Type(), mNumDraws);
 
     switch (curEvent->Type()) {
     case AndroidGeckoEvent::NATIVE_POKE:
         NativeEventCallback();
@@ -433,25 +448,26 @@ nsAppShell::ProcessNextNativeEvent(bool 
 void
 nsAppShell::ResendLastResizeEvent(nsWindow* aDest) {
     if (gLastSizeChange) {
         nsWindow::OnGlobalAndroidEvent(gLastSizeChange);
     }
 }
 
 AndroidGeckoEvent*
-nsAppShell::GetNextEvent()
+nsAppShell::PopNextEvent()
 {
     AndroidGeckoEvent *ae = nsnull;
     MutexAutoLock lock(mQueueLock);
     if (mEventQueue.Length()) {
         ae = mEventQueue[0];
         mEventQueue.RemoveElementAt(0);
         if (ae->Type() == AndroidGeckoEvent::DRAW) {
-            mNumDraws--;
+            if (--mNumDraws == 0)
+                mLastDrawEvent = nsnull;
         }
     }
 
     return ae;
 }
 
 AndroidGeckoEvent*
 nsAppShell::PeekNextEvent()
@@ -483,36 +499,23 @@ nsAppShell::PostEvent(AndroidGeckoEvent 
                 }
             }
         } else {
             mEventQueue.AppendElement(ae);
         }
 
         if (ae->Type() == AndroidGeckoEvent::DRAW) {
             mNumDraws++;
+            mLastDrawEvent = ae;
         }
     }
     NotifyNativeEvent();
 }
 
 void
-nsAppShell::RemoveNextEvent()
-{
-    AndroidGeckoEvent *ae = nsnull;
-    MutexAutoLock lock(mQueueLock);
-    if (mEventQueue.Length()) {
-        ae = mEventQueue[0];
-        mEventQueue.RemoveElementAt(0);
-        if (ae->Type() == AndroidGeckoEvent::DRAW) {
-            mNumDraws--;
-        }
-    }
-}
-
-void
 nsAppShell::OnResume()
 {
 }
 
 nsresult
 nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver)
 {
     NS_ASSERTION(aObserver != nsnull, "nsAppShell::AddObserver: aObserver is null!");
--- a/widget/src/android/nsAppShell.h
+++ b/widget/src/android/nsAppShell.h
@@ -71,34 +71,34 @@ public:
 
     nsresult Init();
 
     void NotifyNativeEvent();
 
     virtual bool ProcessNextNativeEvent(bool mayWait);
 
     void PostEvent(mozilla::AndroidGeckoEvent *event);
-    void RemoveNextEvent();
     void OnResume();
 
     nsresult AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver);
     void CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData);
     void RemoveObserver(const nsAString &aObserverKey);
     void NotifyObservers(nsISupports *aSupports, const char *aTopic, const PRUnichar *aData);
     void ResendLastResizeEvent(nsWindow* aDest);
 
 protected:
     virtual void ScheduleNativeEventCallback();
     virtual ~nsAppShell();
 
     Mutex mQueueLock;
     Mutex mCondLock;
     CondVar mQueueCond;
     int mNumDraws;
+    mozilla::AndroidGeckoEvent *mLastDrawEvent;
     nsTArray<mozilla::AndroidGeckoEvent *> mEventQueue;
     nsInterfaceHashtable<nsStringHashKey, nsIObserver> mObserversHash;
 
-    mozilla::AndroidGeckoEvent *GetNextEvent();
+    mozilla::AndroidGeckoEvent *PopNextEvent();
     mozilla::AndroidGeckoEvent *PeekNextEvent();
 };
 
 #endif // nsAppShell_h__
 
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -1575,16 +1575,67 @@ nsWindow::GetScreenBounds(nsIntRect &aRe
     // with Resize.
     aRect.SizeTo(mBounds.Size());
     LOG(("GetScreenBounds %d,%d | %dx%d\n",
          aRect.x, aRect.y, aRect.width, aRect.height));
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsWindow::GetClientBounds(nsIntRect &aRect)
+{
+    // GetBounds returns a rect whose top left represents the top left of the
+    // outer bounds, but whose width/height represent the size of the inner
+    // bounds (which is messed up).
+    GetBounds(aRect);
+    aRect.MoveBy(GetClientOffset());
+
+    return NS_OK;
+}
+
+nsIntPoint
+nsWindow::GetClientOffset()
+{
+    if (!mIsTopLevel) {
+        return nsIntPoint(0, 0);
+    }
+
+    GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
+
+    GdkAtom type_returned;
+    int format_returned;
+    int length_returned;
+    long *frame_extents;
+
+    if (!mShell || !mShell->window ||
+        !gdk_property_get(mShell->window,
+                          gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
+                          cardinal_atom,
+                          0, // offset
+                          4*4, // length
+                          FALSE, // delete
+                          &type_returned,
+                          &format_returned,
+                          &length_returned,
+                          (guchar **) &frame_extents) ||
+        length_returned/sizeof(glong) != 4) {
+
+        return nsIntPoint(0, 0);
+    }
+
+    // data returned is in the order left, right, top, bottom
+    PRInt32 left = PRInt32(frame_extents[0]);
+    PRInt32 top = PRInt32(frame_extents[2]);
+
+    g_free(frame_extents);
+
+    return nsIntPoint(left, top);
+}
+
+NS_IMETHODIMP
 nsWindow::SetForegroundColor(const nscolor &aColor)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsWindow::SetBackgroundColor(const nscolor &aColor)
 {
@@ -2334,27 +2385,26 @@ nsWindow::OnConfigureEvent(GtkWidget *aW
     //   the client window.
     //
     //   Override-redirect windows are children of the root window so parent
     //   coordinates are root coordinates.
 
     LOG(("configure event [%p] %d %d %d %d\n", (void *)this,
          aEvent->x, aEvent->y, aEvent->width, aEvent->height));
 
-    // mBounds.x/y are set to the window manager frame top-left when Move() or
-    // Resize()d from within Gecko, so comparing with the client window
-    // top-left is weird.  However, mBounds.x/y are set to client window
-    // position below, so this check avoids unwanted rollup on spurious
-    // configure events from Cygwin/X (bug 672103).
-    if (mBounds.x == aEvent->x &&
-        mBounds.y == aEvent->y)
-        return FALSE;
+    nsIntRect screenBounds;
+    GetScreenBounds(screenBounds);
 
     if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) {
-        check_for_rollup(aEvent->window, 0, 0, false, true);
+        // This check avoids unwanted rollup on spurious configure events from
+        // Cygwin/X (bug 672103).
+        if (mBounds.x != screenBounds.x ||
+            mBounds.y != screenBounds.y) {
+            check_for_rollup(aEvent->window, 0, 0, false, true);
+        }
     }
 
     // This event indicates that the window position may have changed.
     // mBounds.Size() is updated in OnSizeAllocate().
 
     // (The gtk_window_get_window_type() function is only available from
     // version 2.20.)
     NS_ASSERTION(GTK_IS_WINDOW(aWidget),
@@ -2372,21 +2422,17 @@ nsWindow::OnConfigureEvent(GtkWidget *aW
         //
         // Skipping the NS_MOVE dispatch saves context menus from an infinite
         // loop when nsXULPopupManager::PopupMoved moves the window to the new
         // position and nsMenuPopupFrame::SetPopupPosition adds
         // offsetForContextMenu on each iteration.
         return FALSE;
     }
 
-    // This is wrong, but noautohide titlebar panels currently depend on it
-    // (bug 601545#c13).  mBounds.TopLeft() should refer to the
-    // window-manager frame top-left, but WidgetToScreenOffset() gives the
-    // client window origin.
-    mBounds.MoveTo(WidgetToScreenOffset());
+    mBounds.MoveTo(screenBounds.TopLeft());
 
     nsGUIEvent event(true, NS_MOVE, this);
 
     event.refPoint = mBounds.TopLeft();
 
     // XXX mozilla will invalidate the entire window after this move
     // complete.  wtf?
     nsEventStatus status;
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -165,16 +165,18 @@ public:
     NS_IMETHOD         PlaceBehind(nsTopLevelWidgetZPlacement  aPlacement,
                                    nsIWidget                  *aWidget,
                                    bool                        aActivate);
     NS_IMETHOD         SetZIndex(PRInt32 aZIndex);
     NS_IMETHOD         SetSizeMode(PRInt32 aMode);
     NS_IMETHOD         Enable(bool aState);
     NS_IMETHOD         SetFocus(bool aRaise = false);
     NS_IMETHOD         GetScreenBounds(nsIntRect &aRect);
+    NS_IMETHOD         GetClientBounds(nsIntRect &aRect);
+    virtual nsIntPoint GetClientOffset();
     NS_IMETHOD         SetForegroundColor(const nscolor &aColor);
     NS_IMETHOD         SetBackgroundColor(const nscolor &aColor);
     NS_IMETHOD         SetCursor(nsCursor aCursor);
     NS_IMETHOD         SetCursor(imgIContainer* aCursor,
                                  PRUint32 aHotspotX, PRUint32 aHotspotY);
     NS_IMETHOD         Invalidate(const nsIntRect &aRect,
                                   bool             aIsSynchronous);
     NS_IMETHOD         Update();
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -764,20 +764,17 @@ NS_METHOD nsWindow::GetBounds(nsIntRect&
 }
 
 //-----------------------------------------------------------------------------
 // Since mBounds contains the dimensions of the client, os2FrameWindow
 // doesn't have to provide any special handling for this method.
 
 NS_METHOD nsWindow::GetClientBounds(nsIntRect& aRect)
 {
-  aRect.x = 0;
-  aRect.y = 0;
-  aRect.width = mBounds.width;
-  aRect.height = mBounds.height;
+  aRect = mBounds;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 nsIntPoint nsWindow::WidgetToScreenOffset()
 {
   POINTL point = { 0, 0 };
--- a/widget/src/windows/nsNativeThemeWin.cpp
+++ b/widget/src/windows/nsNativeThemeWin.cpp
@@ -84,19 +84,21 @@ static inline bool IsHTMLContent(nsIFram
 }
 
 static PRInt32 GetTopLevelWindowActiveState(nsIFrame *aFrame)
 {
   // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
   // until it finds a real window.
   nsIWidget* widget = aFrame->GetNearestWidget();
   nsWindow * window = static_cast<nsWindow*>(widget);
+  if (!window)
+    return mozilla::widget::themeconst::FS_INACTIVE;
   if (widget && !window->IsTopLevelWidget() &&
       !(window = window->GetParentWindow(false)))
-    return false;
+    return mozilla::widget::themeconst::FS_INACTIVE;
 
   if (window->GetWindowHandle() == ::GetActiveWindow())
     return mozilla::widget::themeconst::FS_ACTIVE;
   return mozilla::widget::themeconst::FS_INACTIVE;
 }
 
 static PRInt32 GetWindowFrameButtonState(nsIFrame *aFrame, nsEventStates eventState)
 {
--- a/widget/src/windows/nsTextStore.cpp
+++ b/widget/src/windows/nsTextStore.cpp
@@ -1180,16 +1180,18 @@ nsTextStore::GetScreenExt(TsViewCookie v
   // Result rect is in top level widget coordinates
   refWindow = refWindow->GetTopLevelWindow(false);
   NS_ENSURE_TRUE(refWindow, E_FAIL);
 
   nsIntRect boundRect;
   nsresult rv = refWindow->GetClientBounds(boundRect);
   NS_ENSURE_SUCCESS(rv, E_FAIL);
 
+  boundRect.MoveTo(0, 0);
+
   // Clip frame rect to window rect
   boundRect.IntersectRect(event.mReply.mRect, boundRect);
   if (!boundRect.IsEmpty()) {
     boundRect.MoveBy(refWindow->WidgetToScreenOffset());
     ::SetRect(prc, boundRect.x, boundRect.y,
               boundRect.XMost(), boundRect.YMost());
   } else {
     ::SetRectEmpty(prc);
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -1518,40 +1518,16 @@ NS_METHOD nsWindow::Resize(PRInt32 aX, P
   }
 
   if (aRepaint)
     Invalidate(false);
 
   return NS_OK;
 }
 
-// Resize the client area and position the widget within it's parent
-NS_METHOD nsWindow::ResizeClient(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
-{
-  NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
-  NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
-
-  // Adjust our existing window bounds, based on the new client dims.
-  RECT client;
-  GetClientRect(mWnd, &client);
-  nsIntPoint dims(client.right - client.left, client.bottom - client.top);
-  aWidth = mBounds.width + (aWidth - dims.x);
-  aHeight = mBounds.height + (aHeight - dims.y);
-  
-  if (aX || aY) {
-    // offsets
-    nsIntRect bounds;
-    GetScreenBounds(bounds);
-    aX += bounds.x;
-    aY += bounds.y;
-    return Resize(aX, aY, aWidth, aHeight, aRepaint);
-  }
-  return Resize(aWidth, aHeight, aRepaint);
-}
-
 NS_IMETHODIMP
 nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical)
 {
   NS_ENSURE_ARG_POINTER(aEvent);
 
   if (aEvent->eventStructType != NS_MOUSE_EVENT) {
     // you can only begin a resize drag with a mouse event
     return NS_ERROR_INVALID_ARG;
@@ -1925,19 +1901,19 @@ NS_METHOD nsWindow::GetBounds(nsIntRect 
 
 // Get this component dimension
 NS_METHOD nsWindow::GetClientBounds(nsIntRect &aRect)
 {
   if (mWnd) {
     RECT r;
     VERIFY(::GetClientRect(mWnd, &r));
 
-    // assign size
-    aRect.x = 0;
-    aRect.y = 0;
+    nsIntRect bounds;
+    GetBounds(bounds);
+    aRect.MoveTo(bounds.TopLeft() + GetClientOffset());
     aRect.width  = r.right - r.left;
     aRect.height = r.bottom - r.top;
 
   } else {
     aRect.SetRect(0,0,0,0);
   }
   return NS_OK;
 }
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -120,17 +120,16 @@ public:
   virtual nsIWidget*      GetParent(void);
   virtual float           GetDPI();
   NS_IMETHOD              Show(bool bState);
   NS_IMETHOD              IsVisible(bool & aState);
   NS_IMETHOD              ConstrainPosition(bool aAllowSlop, PRInt32 *aX, PRInt32 *aY);
   NS_IMETHOD              Move(PRInt32 aX, PRInt32 aY);
   NS_IMETHOD              Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
   NS_IMETHOD              Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
-  NS_IMETHOD              ResizeClient(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
   NS_IMETHOD              BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
   NS_IMETHOD              PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget *aWidget, bool aActivate);
   NS_IMETHOD              SetSizeMode(PRInt32 aMode);
   NS_IMETHOD              Enable(bool aState);
   NS_IMETHOD              IsEnabled(bool *aState);
   NS_IMETHOD              SetFocus(bool aRaise);
   NS_IMETHOD              GetBounds(nsIntRect &aRect);
   NS_IMETHOD              GetScreenBounds(nsIntRect &aRect);
--- a/widget/src/xpwidgets/nsBaseWidget.cpp
+++ b/widget/src/xpwidgets/nsBaseWidget.cpp
@@ -295,25 +295,16 @@ ViewWrapper* nsBaseWidget::GetAttachedVi
  }
  
 NS_IMETHODIMP nsBaseWidget::SetAttachedViewPtr(ViewWrapper* aViewWrapper)
  {
    mViewWrapperPtr = aViewWrapper;
    return NS_OK;
  }
 
-NS_METHOD nsBaseWidget::ResizeClient(PRInt32 aX,
-                                     PRInt32 aY,
-                                     PRInt32 aWidth,
-                                     PRInt32 aHeight,
-                                     bool aRepaint)
-{
-  return Resize(aX, aY, aWidth, aHeight, aRepaint);
-}
-
 //-------------------------------------------------------------------------
 //
 // Close this nsBaseWidget
 //
 //-------------------------------------------------------------------------
 NS_METHOD nsBaseWidget::Destroy()
 {
   // Just in case our parent is the only ref to us
@@ -914,16 +905,60 @@ void nsBaseWidget::OnDestroy()
   NS_IF_RELEASE(mContext);
 }
 
 NS_METHOD nsBaseWidget::SetWindowClass(const nsAString& xulWinType)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_METHOD nsBaseWidget::MoveClient(PRInt32 aX, PRInt32 aY)
+{
+  nsIntPoint clientOffset(GetClientOffset());
+  aX -= clientOffset.x;
+  aY -= clientOffset.y;
+  return Move(aX, aY);
+}
+
+NS_METHOD nsBaseWidget::ResizeClient(PRInt32 aWidth,
+                                     PRInt32 aHeight,
+                                     bool aRepaint)
+{
+  NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
+  NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
+
+  nsIntRect clientBounds;
+  GetClientBounds(clientBounds);
+  aWidth = mBounds.width + (aWidth - clientBounds.width);
+  aHeight = mBounds.height + (aHeight - clientBounds.height);
+
+  return Resize(aWidth, aHeight, aRepaint);
+}
+
+NS_METHOD nsBaseWidget::ResizeClient(PRInt32 aX,
+                                     PRInt32 aY,
+                                     PRInt32 aWidth,
+                                     PRInt32 aHeight,
+                                     bool aRepaint)
+{
+  NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
+  NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
+
+  nsIntRect clientBounds;
+  GetClientBounds(clientBounds);
+  aWidth = mBounds.width + (aWidth - clientBounds.width);
+  aHeight = mBounds.height + (aHeight - clientBounds.height);
+
+  nsIntPoint clientOffset(GetClientOffset());
+  aX -= clientOffset.x;
+  aY -= clientOffset.y;
+
+  return Resize(aX, aY, aWidth, aHeight, aRepaint);
+}
+
 //-------------------------------------------------------------------------
 //
 // Bounds
 //
 //-------------------------------------------------------------------------
 
 /**
 * If the implementation of nsWindow supports borders this method MUST be overridden
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -117,20 +117,25 @@ public:
                                           LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                           bool* aAllowRetaining = nsnull);
 
   virtual void            DrawOver(LayerManager* aManager, nsIntRect aRect) {}
   virtual void            UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) {}
   virtual gfxASurface*    GetThebesSurface();
   NS_IMETHOD              SetModal(bool aModal); 
   NS_IMETHOD              SetWindowClass(const nsAString& xulWinType);
+  NS_IMETHOD              MoveClient(PRInt32 aX, PRInt32 aY);
+  NS_IMETHOD              ResizeClient(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
+  NS_IMETHOD              ResizeClient(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
   NS_IMETHOD              SetBounds(const nsIntRect &aRect);
   NS_IMETHOD              GetBounds(nsIntRect &aRect);
   NS_IMETHOD              GetClientBounds(nsIntRect &aRect);
   NS_IMETHOD              GetScreenBounds(nsIntRect &aRect);
+  NS_IMETHOD              GetNonClientMargins(nsIntMargin &margins);
+  NS_IMETHOD              SetNonClientMargins(nsIntMargin &margins);
   virtual nsIntPoint      GetClientOffset();
   NS_IMETHOD              EnableDragDrop(bool aEnable);
   NS_IMETHOD              GetAttention(PRInt32 aCycleCount);
   virtual bool            HasPendingInputEvent();
   NS_IMETHOD              SetIcon(const nsAString &anIconSpec);
   NS_IMETHOD              BeginSecureKeyboardInput();
   NS_IMETHOD              EndSecureKeyboardInput();
   NS_IMETHOD              SetWindowTitlebarColor(nscolor aColor, bool aActive);
@@ -158,19 +163,16 @@ public:
               EVENT_CALLBACK   aHandleEventFunction,
               nsDeviceContext *aContext,
               nsWidgetInitData *aInitData = nsnull,
               bool             aForceUseIWidgetParent = false);
   NS_IMETHOD              SetEventCallback(EVENT_CALLBACK aEventFunction, nsDeviceContext *aContext);
   NS_IMETHOD              AttachViewToTopLevel(EVENT_CALLBACK aViewEventFunction, nsDeviceContext *aContext);
   virtual ViewWrapper*    GetAttachedViewPtr();
   NS_IMETHOD              SetAttachedViewPtr(ViewWrapper* aViewWrapper);
-  NS_IMETHOD              ResizeClient(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint);
-  NS_IMETHOD              GetNonClientMargins(nsIntMargin &margins);
-  NS_IMETHOD              SetNonClientMargins(nsIntMargin &margins);
   NS_IMETHOD              RegisterTouchWindow();
   NS_IMETHOD              UnregisterTouchWindow();
 
   nsPopupLevel PopupLevel() { return mPopupLevel; }
 
   virtual nsIntSize       ClientToWindowSize(const nsIntSize& aClientSize)
   {
     return aClientSize;
--- a/xpcom/base/nsStackWalk.cpp
+++ b/xpcom/base/nsStackWalk.cpp
@@ -36,19 +36,157 @@
  * 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 ***** */
 
 /* API for getting a stack trace of the C/C++ stack on the current thread */
 
 #include "mozilla/Util.h"
+#include "nsDebug.h"
+#include "nsStackWalkPrivate.h"
 
 #include "nsStackWalk.h"
 
+// The presence of this address is the stack must stop the stack walk. If
+// there is no such address, the structure will be {NULL, true}.
+struct CriticalAddress {
+  void* mAddr;
+  bool mInit;
+};
+static CriticalAddress gCriticalAddress;
+
+#if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
+#include <dlfcn.h>
+#endif
+
+#ifdef XP_MACOSX
+#include <pthread.h>
+#include <errno.h>
+#include <CoreServices/CoreServices.h>
+
+typedef void
+malloc_logger_t(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
+                uintptr_t result, uint32_t num_hot_frames_to_skip);
+extern malloc_logger_t *malloc_logger;
+
+static void
+stack_callback(void *pc, void *closure)
+{
+  const char *name = reinterpret_cast<char *>(closure);
+  Dl_info info;
+
+  // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
+  // stack shows up as having two pthread_cond_wait$UNIX2003 frames. The
+  // correct one is the first that we find on our way up, so the
+  // following check for gCriticalAddress.mAddr is critical.
+  if (gCriticalAddress.mAddr || dladdr(pc, &info) == 0  ||
+      info.dli_sname == NULL || strcmp(info.dli_sname, name) != 0)
+    return;
+  gCriticalAddress.mAddr = pc;
+}
+
+#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
+#define MAC_OS_X_VERSION_10_6_HEX 0x00001060
+
+static PRInt32 OSXVersion()
+{
+  static PRInt32 gOSXVersion = 0x0;
+  if (gOSXVersion == 0x0) {
+    OSErr err = ::Gestalt(gestaltSystemVersion, (SInt32*)&gOSXVersion);
+    MOZ_ASSERT(err == noErr);
+  }
+  return gOSXVersion;
+}
+
+static bool OnLionOrLater()
+{
+  return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
+}
+
+static bool OnSnowLeopardOrLater()
+{
+  return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
+}
+
+static void
+my_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
+                 uintptr_t result, uint32_t num_hot_frames_to_skip)
+{
+  static bool once = false;
+  if (once)
+    return;
+  once = true;
+
+  // On Leopard dladdr returns the wrong value for "new_sem_from_pool". The
+  // stack shows up as having two pthread_cond_wait$UNIX2003 frames.
+  const char *name = OnSnowLeopardOrLater() ? "new_sem_from_pool" :
+    "pthread_cond_wait$UNIX2003";
+  NS_StackWalk(stack_callback, 0, const_cast<char*>(name));
+}
+
+void
+StackWalkInitCriticalAddress()
+{
+  if(gCriticalAddress.mInit)
+    return;
+  gCriticalAddress.mInit = true;
+  // We must not do work when 'new_sem_from_pool' calls realloc, since
+  // it holds a non-reentrant spin-lock and we will quickly deadlock.
+  // new_sem_from_pool is not directly accessible using dlsym, so
+  // we force a situation where new_sem_from_pool is on the stack and
+  // use dladdr to check the addresses.
+
+  MOZ_ASSERT(malloc_logger == NULL);
+  malloc_logger = my_malloc_logger;
+
+  pthread_cond_t cond;
+  int r = pthread_cond_init(&cond, 0);
+  MOZ_ASSERT(r == 0);
+  pthread_mutex_t mutex;
+  r = pthread_mutex_init(&mutex,0);
+  MOZ_ASSERT(r == 0);
+  r = pthread_mutex_lock(&mutex);
+  MOZ_ASSERT(r == 0);
+  struct timespec abstime = {0, 1};
+  r = pthread_cond_timedwait_relative_np(&cond, &mutex, &abstime);
+  malloc_logger = NULL;
+
+  // On Lion, malloc is no longer called from pthread_cond_*wait*. This prevents
+  // us from finding the address, but that is fine, since with no call to malloc
+  // there is no critical address.
+  MOZ_ASSERT(OnLionOrLater() || gCriticalAddress.mAddr != NULL);
+  MOZ_ASSERT(r == ETIMEDOUT);
+  r = pthread_mutex_unlock(&mutex);
+  MOZ_ASSERT(r == 0);
+  r = pthread_mutex_destroy(&mutex);
+  MOZ_ASSERT(r == 0);
+  r = pthread_cond_destroy(&cond);
+  MOZ_ASSERT(r == 0);
+}
+
+static bool IsCriticalAddress(void* aPC)
+{
+  return gCriticalAddress.mAddr == aPC;
+}
+#else
+static bool IsCriticalAddress(void* aPC)
+{
+  return false;
+}
+// We still initialize gCriticalAddress.mInit so that this code behaves
+// the same on all platforms. Otherwise a failure to init would be visible
+// only on OS X.
+void
+StackWalkInitCriticalAddress()
+{
+  gCriticalAddress.mInit = true;
+}
+#endif
+
 #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_IA64)) // WIN32 x86 stack walking code
 
 #include "nscore.h"
 #include <windows.h>
 #include <process.h>
 #include <stdio.h>
 #include "plstr.h"
 #include "mozilla/FunctionTimer.h"
@@ -650,16 +788,17 @@ WalkStackThread(void* aData)
  * Otherwise StackWalk will return FALSE when it hits a frame in a DLL
  * whose in memory address doesn't match its in-file address.
  */
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure)
 {
+    MOZ_ASSERT(gCriticalAddress.mInit);
     HANDLE myProcess, myThread;
     DWORD walkerReturn;
     struct WalkStackData data;
 
     if (!EnsureImageHlpInitialized())
         return false;
 
     // Have to duplicate handle to get a real handle.
@@ -1135,22 +1274,16 @@ NS_FormatCodeAddressDetails(void *aPC, c
 // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
 // if __USE_GNU is defined.  I suppose its some kind of standards
 // adherence thing.
 //
 #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
 #define __USE_GNU
 #endif
 
-#if defined(HAVE_DLOPEN) || defined(XP_MACOSX)
-#include <dlfcn.h>
-#endif
-
-
-
 // This thing is exported by libstdc++
 // Yes, this is a gcc only hack
 #if defined(MOZ_DEMANGLE_SYMBOLS)
 #include <cxxabi.h>
 #include <stdlib.h> // for free()
 #endif // MOZ_DEMANGLE_SYMBOLS
 
 void DemangleSymbol(const char * aSymbol, 
@@ -1343,16 +1476,17 @@ cs_operate(int (*operate_func)(void *, v
 {
     cswalkstack(csgetframeptr(), operate_func, usrarg);
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure)
 {
+    MOZ_ASSERT(gCriticalAddress.mInit);
     struct my_user_args args;
 
     if (!initialized)
         myinit();
 
     args.callback = aCallback;
     args.skipFrames = aSkipFrames; /* XXX Not handled! */
     args.closure = aClosure;
@@ -1417,68 +1551,22 @@ NS_FormatCodeAddressDetails(void *aPC, c
 #else
 #define HAVE___LIBC_STACK_END 0
 #endif
 
 #if HAVE___LIBC_STACK_END
 extern void *__libc_stack_end; // from ld-linux.so
 #endif
 
-#ifdef XP_MACOSX
-struct AddressRange {
-  void* mStart;
-  void* mEnd;
-};
-// Addresses in this range must stop the stack walk
-static AddressRange gCriticalRange;
-
-static void FindFunctionAddresses(const char* aName, AddressRange* aRange)
-{
-  aRange->mStart = dlsym(RTLD_DEFAULT, aName);
-  if (!aRange->mStart)
-    return;
-  aRange->mEnd = aRange->mStart;
-  while (true) {
-    Dl_info info;
-    if (!dladdr(aRange->mEnd, &info))
-      break;
-    if (strcmp(info.dli_sname, aName))
-      break;
-    aRange->mEnd = (char*)aRange->mEnd + 1;
-  }
-}
-
-static void InitCriticalRanges()
-{
-  if (gCriticalRange.mStart)
-    return;
-  // We must not do work when 'new_sem_from_pool' calls realloc, since
-  // it holds a non-reentrant spin-lock and we will quickly deadlock.
-  // new_sem_from_pool is not directly accessible using dladdr but its
-  // code is bundled with pthread_cond_wait$UNIX2003 (on
-  // Leopard anyway).
-  FindFunctionAddresses("pthread_cond_wait$UNIX2003", &gCriticalRange);
-}
-
-static bool InCriticalRange(void* aPC)
-{
-  return gCriticalRange.mStart &&
-    gCriticalRange.mStart <= aPC && aPC < gCriticalRange.mEnd;
-}
-#else
-static void InitCriticalRanges() {}
-static bool InCriticalRange(void* aPC) { return false; }
-#endif
-
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure)
 {
+  MOZ_ASSERT(gCriticalAddress.mInit);
   // Stack walking code courtesy Kipp's "leaky".
-  InitCriticalRanges();
 
   // Get the frame pointer
   void **bp;
 #if defined(__i386) 
   __asm__( "movl %%ebp, %0" : "=g"(bp));
 #else
   // It would be nice if this worked uniformly, but at least on i386 and
   // x86_64, it stopped working with gcc 4.1, because it points to the
@@ -1501,18 +1589,18 @@ NS_StackWalk(NS_WalkStackCallback aCallb
       break;
     }
 #if (defined(__ppc__) && defined(XP_MACOSX)) || defined(__powerpc64__)
     // ppc mac or powerpc64 linux
     void *pc = *(bp+2);
 #else // i386 or powerpc32 linux
     void *pc = *(bp+1);
 #endif
-    if (InCriticalRange(pc)) {
-      printf("Aborting stack trace, PC in critical range\n");
+    if (IsCriticalAddress(pc)) {
+      printf("Aborting stack trace, PC is critical\n");
       return NS_ERROR_UNEXPECTED;
     }
     if (--skip < 0) {
       (*aCallback)(pc, aClosure);
     }
     bp = next;
   }
   return NS_OK;
@@ -1528,34 +1616,42 @@ struct unwind_info {
     int skip;
     void *closure;
 };
 
 static _Unwind_Reason_Code
 unwind_callback (struct _Unwind_Context *context, void *closure)
 {
     unwind_info *info = static_cast<unwind_info *>(closure);
-    if (--info->skip < 0) {
-        void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
+    void *pc = reinterpret_cast<void *>(_Unwind_GetIP(context));
+    if (IsCriticalAddress(pc)) {
+        printf("Aborting stack trace, PC is critical\n");
+        /* We just want to stop the walk, so any error code will do.
+           Using _URC_NORMAL_STOP would probably be the most accurate,
+           but it is not defined on Android for ARM. */
+        return _URC_FOREIGN_EXCEPTION_CAUGHT;
+    }
+    if (--info->skip < 0)
         (*info->callback)(pc, info->closure);
-    }
     return _URC_NO_REASON;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure)
 {
+    MOZ_ASSERT(gCriticalAddress.mInit);
     unwind_info info;
     info.callback = aCallback;
     info.skip = aSkipFrames + 1;
     info.closure = aClosure;
 
-    _Unwind_Backtrace(unwind_callback, &info);
-
+    _Unwind_Reason_Code t = _Unwind_Backtrace(unwind_callback, &info);
+    if (t != _URC_END_OF_STACK)
+        return NS_ERROR_UNEXPECTED;
     return NS_OK;
 }
 
 #endif
 
 EXPORT_XPCOM_API(nsresult)
 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
 {
@@ -1615,16 +1711,17 @@ NS_FormatCodeAddressDetails(void *aPC, c
 #endif
 
 #else // unsupported platform.
 
 EXPORT_XPCOM_API(nsresult)
 NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames,
              void *aClosure)
 {
+    MOZ_ASSERT(gCriticalAddress.mInit);
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 EXPORT_XPCOM_API(nsresult)
 NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails)
 {
     aDetails->library[0] = '\0';
     aDetails->loffset = 0;
new file mode 100644
--- /dev/null
+++ b/xpcom/base/nsStackWalkPrivate.h
@@ -0,0 +1,43 @@
+/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
+/* ***** 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 NS_WalkTheStack.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mozilla Corporation (original author)
+ *
+ * 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 ***** */
+
+/**
+ * Initialize the critical sections for this platform so that we can
+ * abort stack walks when needed.
+ */
+void
+StackWalkInitCriticalAddress(void);
--- a/xpcom/base/nsTraceRefcntImpl.cpp
+++ b/xpcom/base/nsTraceRefcntImpl.cpp
@@ -45,16 +45,17 @@
 #include "prprf.h"
 #include "prlog.h"
 #include "plstr.h"
 #include "prlink.h"
 #include <stdlib.h>
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include <math.h>
+#include "nsStackWalkPrivate.h"
 #include "nsStackWalk.h"
 #include "nsString.h"
 
 #include "nsXULAppAPI.h"
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
@@ -918,16 +919,18 @@ nsTraceRefcntImpl::DemangleSymbol(const 
 }
 
 
 //----------------------------------------------------------------------
 
 EXPORT_XPCOM_API(void)
 NS_LogInit()
 {
+  // FIXME: This is called multiple times, we should probably not allow that.
+  StackWalkInitCriticalAddress();
 #ifdef NS_IMPL_REFCNT_LOGGING
   if (++gInitCount)
     nsTraceRefcntImpl::SetActivityIsLegal(true);
 #endif
 
 #ifdef NS_TRACE_MALLOC
   // XXX we don't have to worry about shutting down trace-malloc; it
   // handles this itself, through an atexit() callback.
deleted file mode 100644
--- a/xpinstall/Makefile.in
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# ***** 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 Communicator client code, released
-# March 31, 1998.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Daniel Veditz <dveditz@netscape.com>
-#   Douglas Turner <dougt@netscape.com>
-#   Samir Gehani <sgehani@netscape.com>
-#   Dave Townsend <dtownsend@oxymoronical.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE		= xpinstall
-DIRS		= public src
-
-ifdef ENABLE_TESTS
-DIRS += tests
-endif
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/xpinstall/public/Makefile.in
+++ /dev/null
@@ -1,65 +0,0 @@
-#
-# ***** 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 Communicator client code, released
-# March 31, 1998.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Daniel Veditz <dveditz@netscape.com>
-#   Douglas Turner <dougt@netscape.com>
-#   Dave Townsend <dtownsend@oxymoronical.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE		= xpinstall
-GRE_MODULE	= 1
-
-XPIDLSRCS	= \
-		nsIXPIDialogService.idl \
-		nsIXPIProgressDialog.idl \
-		nsIXPInstallManager.idl \
-		nsIXPIInstallInfo.idl \
-		nsPICertNotification.idl \
-		$(NULL)
-
-EXPORTS		= \
-		nsIDOMInstallTriggerGlobal.h \
-		nsSoftwareUpdateIIDs.h \
-		$(NULL)
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/xpinstall/public/nsIDOMInstallTriggerGlobal.h
+++ /dev/null
@@ -1,82 +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) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 nsIDOMInstallTriggerGlobal_h__
-#define nsIDOMInstallTriggerGlobal_h__
-
-#include "nsISupports.h"
-#include "nsString.h"
-#include "nsIScriptContext.h"
-#include "nsXPITriggerInfo.h"
-#include "nsIXPIInstallInfo.h"
-
-
-#define NS_IDOMINSTALLTRIGGERGLOBAL_IID \
- { 0x23bb93a4, 0xdaee, 0x4a47, \
-  {0x87, 0x76, 0xb1, 0x72, 0x35, 0x86, 0x2d, 0xac}}
-
-class nsIDOMInstallTriggerGlobal : public nsISupports {
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMINSTALLTRIGGERGLOBAL_IID)
-  enum {
-    NOT_FOUND = -5,
-    MAJOR_DIFF = 4,
-    MINOR_DIFF = 3,
-    REL_DIFF = 2,
-    BLD_DIFF = 1,
-    EQUAL = 0
-  };
-
-  NS_IMETHOD    GetOriginatingURI(nsIScriptGlobalObject* aGlobalObject, nsIURI * *aUri)=0;
-
-  NS_IMETHOD    UpdateEnabled(nsIScriptGlobalObject* aGlobalObject, bool aUseWhitelist, bool* aReturn)=0;
-
-  NS_IMETHOD    UpdateEnabled(nsIURI* aURI, bool aUseWhitelist, bool* aReturn)=0;
-
-  NS_IMETHOD    StartInstall(nsIXPIInstallInfo* aInstallInfo, bool* aReturn)=0;
-
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMInstallTriggerGlobal,
-                              NS_IDOMINSTALLTRIGGERGLOBAL_IID)
-
-extern nsresult NS_InitInstallTriggerGlobalClass(nsIScriptContext *aContext, void **aPrototype);
-
-extern "C" nsresult NS_NewScriptInstallTriggerGlobal(nsIScriptContext *aContext, nsISupports *aSupports, nsISupports *aParent, void **aReturn);
-
-#endif // nsIDOMInstallTriggerGlobal_h__
deleted file mode 100644
--- a/xpinstall/public/nsIXPIDialogService.idl
+++ /dev/null
@@ -1,107 +0,0 @@
-/* ***** 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 Mozilla XPInstall.
- *
- * 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 Veditz <dveditz@netscape.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 "nsISupports.idl"
-
-interface nsIDOMWindow;
-interface nsIXPIProgressDialog;
-interface nsIObserver;
-
-/**
- * A service provided by embedding applications to override
- * the default XUL implmementation of XPInstall dialogs.
- *
- * Embedding applications which wish to override the default
- * XUL dialogs need to create a component which implements
- * this interface and registers with the Contract ID
- * "@mozilla.org/embedui/xpinstall-dialog-service;1"
- */
-[scriptable, uuid(8cdd8baa-1dd2-11b2-909a-f0178da5c5ff)]
-interface nsIXPIDialogService : nsISupports
-{
-    /**
-     * @brief  Ask the user if it's OK to install
-     *
-     * When called the XPIDialogService implementation should pose an
-     * install confirmation dialog and return the user's response
-     *
-     * @param parent        a window that can be used to parent the modal dialog
-     *
-     * @param packageList   For each install package there will be three strings,
-     *                      a display name, a source URL, and a the name of the
-     *                      organization that signed this install.  Note that the
-     *                      name of the signer is not verified.  Verification
-     *                      happens when the the install has completely downloaded.
-     *                      Your user interface should only suggest that the 
-     *                      install may be signed by this organization name.
-     *                      Note that an unsigned archive is indicated by an 
-     *                      empty string.   
-     *
-     * @param count         The number of strings in the packageList. This
-     *                      will always be three times the number of 
-     *                      packages.
-     *
-     * @return true to install, false to cancel
-     */
-    boolean confirmInstall(in nsIDOMWindow parent,
-                           [array, size_is(count)] in wstring packageList,
-                           in unsigned long count);
-
-    /**
-     * @brief Create and open a download-and-install progress dialog
-     *
-     * When called the XPIDialogService implementation creates and opens
-     * a dialog to display the status of the install. When the dialog
-     * is ready to be used then the observer must be called: the subject
-     * is an nsIXPIProgressDialog that nsXPInstallManager can use to control
-     * the dialog, the topic is "xpinstall-progress"  and the data is "open".
-     *
-     * If the user wishes to cancel the download, the dialog can call the
-     * observe method with the same subject and topic and the data "cancel".
-     *
-     * @note Unless this routine throws an exception the observer <b>must</b>
-     * be called or nsXPInstallManager will wait forever and never clean
-     * itself up.
-     *
-     * @param packageList   three strings per package as in confirmInstall()
-     * @param count         the number of strings in the list
-     * @param observer      nsIObserver to receive messages from the dialog
-     */
-    void openProgressDialog([array, size_is(count)] in wstring packageList,
-                            in unsigned long count,
-                            in nsIObserver observer);
-};
deleted file mode 100644
--- a/xpinstall/public/nsIXPIInstallInfo.idl
+++ /dev/null
@@ -1,74 +0,0 @@
-/* ***** 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 XPInstall.
- *
- * The Initial Developer of the Original Code is
- * Dave Townsend <dtownsend@oxymoronical.com>.
- *
- * Portions created by the Initial Developer are Copyright (C) 2007
- * 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 *****
- */
-
-#include "nsISupports.idl"
-
-[ptr] native triggerInfoPtr(nsXPITriggerInfo);
-
-interface nsIDOMWindow;
-interface nsIDocShell;
-interface nsIURI;
-
-/**
- * Interface holding information about a triggered install that can be passed
- * to and from script.
- */
-[scriptable, uuid(5a4a775c-e452-4cf2-8ff8-d327ae24aec6)]
-interface nsIXPIInstallInfo : nsISupports
-{
-  /**
-   * The install triggers supplied by the install.
-   */
-  [noscript, notxpcom] attribute triggerInfoPtr triggerInfo;
-  
-  /**
-   * The original window that initiated the install.
-   */
-  readonly attribute nsIDOMWindow originatingWindow;
-  
-  /**
-   * The original URI calling the install. This is the URI that would have been
-   * checked against the whitelist if necessary.
-   */
-  readonly attribute nsIURI originatingURI;
-  
-  /**
-   * The chome type of the install.
-   */
-  readonly attribute PRUint32 chromeType;
-};
deleted file mode 100644
--- a/xpinstall/public/nsIXPIProgressDialog.idl
+++ /dev/null
@@ -1,80 +0,0 @@
-/* ***** 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 Mozilla XPInstall.
- *
- * 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 Veditz <dveditz@netscape.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 "nsISupports.idl"
-
-/**
- * Interface to display XPInstall download and install status.
- */
-[scriptable, uuid(ce8f744e-d5a5-41b3-911f-0fee3008b64e)]
-interface nsIXPIProgressDialog : nsISupports
-{
-    /**
-     * state values for onStateChange
-     */
-    const short DOWNLOAD_START       = 0;
-    const short DOWNLOAD_DONE        = 1;
-    const short INSTALL_START        = 2;
-    const short INSTALL_DONE         = 3;
-    const short DIALOG_CLOSE         = 4;
-
-    /**
-     * basic info to control the install progress dialog. The dialog can
-     * go away any time after it has received the DIALOG_CLOSE state message
-     * but needs to accept messages until that time even if it is not visible.
-     *
-     * Normally for each install package the dialog will receive the download
-     * and install messages in START/DONE pairs, but in the case of a download
-     * error the dialog will be sent only the DOWNLOAD_START followed by an
-     * INSTALL_DONE message with the value nsInstall::DOWNLOAD_ERROR
-     *
-     * @param index     the package this message is about of those passed into
-     *                  openProgressDialog. ignored when state==DIALOG_CLOSE
-     * @param state     the kind of message
-     * @param value     final result when state==INSTALL_DONE, otherwise ignored
-     */
-    void onStateChange( in unsigned long index, in short state, in long value );
-
-    /**
-     * download progress
-     *
-     * @param index     the package to which this refers
-     * @param value     number of bytes downloaded
-     * @param maxValue  the total size
-     */
-    void onProgress( in unsigned long index, in unsigned long long value, in unsigned long long maxValue );
-};
deleted file mode 100644
--- a/xpinstall/public/nsIXPInstallManager.idl
+++ /dev/null
@@ -1,84 +0,0 @@
-/* ***** 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 Mozilla XPInstall.
- *
- * 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@mozilla.org>
- *
- * 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 "nsISupports.idl"
-
-interface nsIXPIProgressDialog;
-interface nsIXPIInstallInfo;
-
-/**
- * Interface to XPInstallManager - manages download and install operations.
- */
-[scriptable, uuid(83fdd52f-2d34-4e22-981d-cf3c4ae76faa)]
-interface nsIXPInstallManager : nsISupports
-{
-    /** 
-     * Initiates a download and install operation of the supplied URLs
-     * and sends notifications to the supplied listener.
-     * @param aURLs     array of XPI urls to download and install
-     * @param aURLCount number of XPI urls in aURLs
-     * @param aListener a listener to receive status notifications
-     */
-    void initManagerFromChrome([array, size_is(aURLCount)] in wstring aURLs,
-                               in unsigned long aURLCount,
-                               in nsIXPIProgressDialog aListener);
-    /**
-     * Initiates a set of downloads and checks the supplied hashes after
-     * download. Just like initManagerFromChrome() in all other respects
-     * @param aURLs     array of XPI urls to download and install
-     * @param aHashes   array of hash strings to validate. The entire array
-     *                  or individual hashes can be null to indicate no
-     *                  checking. If supplied looks like "type:hash", like
-     *                  "md5:3232bc5624041c507db0965324188024".
-     *                  Supports the types in nsICryptoHash
-     * @param aURLCount number of XPI urls in aURLs and aHashes
-     * @param aListener a listener to receive status notifications
-     */
-    void initManagerWithHashes([array, size_is(aURLCount)] in wstring aURLs,
-                               [array, size_is(aURLCount)] in string aHashes,
-                               in unsigned long aURLCount,
-                               in nsIXPIProgressDialog aListener);
-    
-    /**
-     * Initiates a set of downloads based on an install info object. Will
-     * display confirmation dialog as if the install info had been supplied
-     * by content.
-     * @param aInstallInfo The install info object providing install triggers
-     *                     and script context for the install.
-     */
-    void initManagerWithInstallInfo(in nsIXPIInstallInfo aInstallInfo);
-};
-
deleted file mode 100644
--- a/xpinstall/public/nsPICertNotification.idl
+++ /dev/null
@@ -1,48 +0,0 @@
-/* ***** 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 XPInstall Signing.
- *
- * The Initial Developer of the Original Code is Doug Turner.
- * 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 ***** */
-
-#include "nsISupports.idl"
-
-interface nsIURI;
-interface nsIPrincipal;
-
-[uuid(42cd7162-ea4a-4088-9888-63ea5095869e)]
-interface nsPICertNotification : nsISupports
-{
-    void onCertAvailable(in nsIURI aURI, 
-                         in nsISupports aContext, 
-                         in PRUint32 aStatus,
-                         in nsIPrincipal aPrincipal);
-};
deleted file mode 100644
--- a/xpinstall/public/nsSoftwareUpdateIIDs.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *   Douglas Turner <dougt@netscape.com>
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 nsSoftwareUpdateIIDs_h___
-#define nsSoftwareUpdateIIDs_h___
-
-#define NS_SoftwareUpdateInstallTrigger_CID          \
-{ /* 18c2f98d-b09f-11d2-bcde-00805f0e1353 */         \
-    0x18c2f98d,                                      \
-    0xb09f,                                          \
-    0x11d2,                                          \
-    {0xbc, 0xde, 0x00, 0x80, 0x5f, 0x0e, 0x13, 0x53} \
-}
-
-#define NS_XPInstallManager_CID                      \
-{ /* {6a4d4c1e-a74a-4320-8124-16233a0183d6} */       \
-    0x6a4d4c1e,                                      \
-    0xa74a,                                          \
-    0x4320,                                          \
-    { 0x81, 0x24, 0x16, 0x23, 0x3a, 0x1, 0x83, 0xd6} \
-}
-
-
-#endif /* nsSoftwareUpdateIIDs_h___ */
-
deleted file mode 100644
--- a/xpinstall/public/xpinstall.js
+++ /dev/null
@@ -1,2 +0,0 @@
-pref("xpinstall.enabled",       true);
-pref("xpinstall.whitelist.required", true);
deleted file mode 100644
--- a/xpinstall/src/CertReader.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-/* ***** 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 XPInstall Signing.
- *
- * The Initial Developer of the Original Code is Doug Turner.
- * 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 ***** */
-
-#include "zlib.h"
-#include "zipstruct.h"
-
-#include "CertReader.h"
-
-#include "nsCRT.h"
-#include "nsIServiceManager.h"
-#include "nsISignatureVerifier.h"
-#include "nsIInputStream.h"
-#include "nsIPrincipal.h"
-#include "nsIURI.h"
-#include "nsPICertNotification.h"
-
-
-#include "nsNetUtil.h"
-
-// just a guess at the max size of the cert.
-#define MAX_SIGNATURE_SIZE (32*1024)
-
-
-/*
- *  x t o i n t
- *
- *  Converts a two byte ugly endianed integer
- *  to our platform's integer.
- *
- */
-
-static unsigned int xtoint (unsigned char *ii)
-{
-  return (int) (ii [0]) | ((int) ii [1] << 8);
-}
-
-/*
- *  x t o l o n g
- *
- *  Converts a four byte ugly endianed integer
- *  to our platform's integer.
- *
- */
-
-static unsigned long xtolong (unsigned char *ll)
-{
-  unsigned long ret;
-
-  ret =  ((((unsigned long) ll [0]) <<  0) |
-          (((unsigned long) ll [1]) <<  8) |
-          (((unsigned long) ll [2]) << 16) |
-          (((unsigned long) ll [3]) << 24) );
-
-  return ret;
-}
-
-static int my_inflate(unsigned char* compr, PRUint32 comprLen, unsigned char* uncompr, PRUint32 uncomprLen)
-{
-  int err;
-  z_stream d_stream; /* decompression stream */
-  memset (&d_stream, 0, sizeof (d_stream));
-
-  // buffer is way to small to even deal with.
-  if (uncomprLen < 10)
-    return -1;
-
-  *uncompr = '\0';
-
-  if (inflateInit2 (&d_stream, -MAX_WBITS) != Z_OK)
-    return -1;
-
-  d_stream.next_in  = compr;
-  d_stream.avail_in = (uInt)comprLen;
-
-  d_stream.next_out = uncompr;
-  d_stream.avail_out = (uInt)uncomprLen;
-
-  err = inflate(&d_stream, Z_NO_FLUSH);
-
-  if (err != Z_OK && err != Z_STREAM_END) {
-    inflateEnd(&d_stream);
-    return -1;
-  }
-
-  err = inflateEnd(&d_stream);
-  if (err != Z_OK) {
-    return -1;
-  }
-  return 0;
-}
-
-CertReader::CertReader(nsIURI* aURI, nsISupports* aContext, nsPICertNotification* aObs):
-  mContext(aContext),
-  mURI(aURI),
-  mObserver(aObs)
-{
-}
-
-CertReader::~CertReader()
-{
-}
-
-NS_IMPL_ISUPPORTS2(CertReader, nsIStreamListener, nsIRequestObserver)
-
-NS_IMETHODIMP
-CertReader::OnStartRequest(nsIRequest *request, nsISupports* context)
-{
-  mVerifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID);
-  if (!mVerifier)
-    return NS_BINDING_ABORTED;
-
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  nsresult rv = request->GetLoadGroup(getter_AddRefs(loadGroup));
-  if (NS_SUCCEEDED(rv) && loadGroup)
-    loadGroup->RemoveRequest(request, nsnull, NS_BINDING_RETARGETED);
-
-  mLeftoverBuffer.Truncate();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-CertReader::OnDataAvailable(nsIRequest *request,
-                            nsISupports* context,
-                            nsIInputStream *aIStream,
-                            PRUint32 aSourceOffset,
-                            PRUint32 aLength)
-{
-  if (!mVerifier)
-    return NS_BINDING_ABORTED;
-
-  char buf[4096];
-  PRUint32 amt, size;
-  nsresult rv;
-
-  while (aLength)
-  {
-    size = NS_MIN(aLength, sizeof(buf));
-
-    rv = aIStream->Read(buf, size, &amt);
-    if (NS_FAILED(rv))
-      return rv;
-
-    aLength -= amt;
-
-    mLeftoverBuffer.Append(buf, amt);
-
-    if (mLeftoverBuffer.Length() < ZIPLOCAL_SIZE)
-      continue;
-
-    const char* caret = mLeftoverBuffer.get();
-
-    ZipLocal_* ziplocal = (ZipLocal_*) caret;
-
-    if (xtolong(ziplocal->signature) != LOCALSIG)
-      return NS_BINDING_ABORTED;
-
-    // did we read the entire file entry into memory?
-    PRUint32 fileEntryLen = (ZIPLOCAL_SIZE +
-                             xtoint(ziplocal->filename_len) +
-                             xtoint(ziplocal->extrafield_len) +
-                             xtolong(ziplocal->size));
-
-
-    // prevent downloading a huge file on an unsigned cert
-    if (fileEntryLen > MAX_SIGNATURE_SIZE)
-      return NS_BINDING_ABORTED;
-
-    if (mLeftoverBuffer.Length() < fileEntryLen)
-    {
-      // we are just going to buffer and continue.
-      continue;
-    }
-
-    // the assumption here is that we have the fileEntry in mLeftoverBuffer
-
-    int err = 0;
-    unsigned char* orgData = nsnull;
-    unsigned char* sigData = nsnull;
-    const char* data = (caret +
-                        ZIPLOCAL_SIZE +
-                        xtoint(ziplocal->filename_len) +
-                        xtoint(ziplocal->extrafield_len));
-
-    PRUint32 sigSize = 0;
-    PRUint32 orgSize = xtolong ((unsigned char *) ziplocal->orglen);
-    PRUint32 cSize   = xtolong ((unsigned char *) ziplocal->size);
-
-    switch (xtoint(ziplocal->method))
-    {
-      case STORED:
-        // file is uncompressed, can use the data where it is
-        sigSize = cSize;
-        sigData = (unsigned char*)data;
-        break;
-
-      case DEFLATED:
-        if (orgSize == 0 || orgSize > MAX_SIGNATURE_SIZE)
-          return NS_BINDING_ABORTED;
-
-        orgData = (unsigned char*)malloc(orgSize);
-        if (!orgData)
-          return NS_BINDING_ABORTED;
-
-        err = my_inflate((unsigned char*)data,
-                         cSize,
-                         orgData,
-                         orgSize);
-
-        sigSize = orgSize;
-        sigData = orgData;
-        break;
-
-      default:
-        // unsupported compression method
-        err = Z_DATA_ERROR;
-        break;
-    }
-
-    if (err == 0)
-    {
-      PRInt32 verifyError;
-      rv = mVerifier->VerifySignature((char*)sigData, sigSize, nsnull, 0,
-                                 &verifyError, getter_AddRefs(mPrincipal));
-    }
-    if (orgData)
-        free(orgData);
-
-    // Cancel the load now that we've verified the signature
-    return NS_BINDING_ABORTED;
-  }
-
-  return NS_OK; // continue reading
-}
-
-NS_IMETHODIMP
-CertReader::OnStopRequest(nsIRequest *request, nsISupports* context,
-                          nsresult aStatus)
-{
-    mObserver->OnCertAvailable(mURI,
-                               mContext,
-                               aStatus,
-                               mPrincipal);
-
-    return NS_OK;
-}
-
-
deleted file mode 100644
--- a/xpinstall/src/CertReader.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* ***** 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 XPInstall Signing.
- *
- * The Initial Developer of the Original Code is Doug Turner.
- * 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 ***** */
-
-#include "nsCOMPtr.h"
-#include "nsIStreamListener.h"
-#include "nsString.h"
-
-class nsISignatureVerifier;
-class nsIPrincipal;
-class nsIURI;
-class nsPICertNotification;
-
-
-class CertReader : public nsIStreamListener
-{
-public:
-  CertReader(nsIURI* uri, nsISupports* aContext, nsPICertNotification* aObs);
-  virtual ~CertReader();
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSISTREAMLISTENER
-
-private:
-  nsCString mLeftoverBuffer;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsISignatureVerifier> mVerifier;
-
-  nsCOMPtr<nsISupports> mContext;
-  nsCOMPtr<nsIURI> mURI;
-  nsCOMPtr<nsPICertNotification> mObserver;
-};
deleted file mode 100644
--- a/xpinstall/src/Makefile.in
+++ /dev/null
@@ -1,74 +0,0 @@
-#
-# ***** 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 Communicator client code, released
-# March 31, 1998.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#   Daniel Veditz <dveditz@netscape.com>
-#   Douglas Turner <dougt@netscape.com>
-#   Dave Townsend <dtownsend@oxymoronical.com>
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE		= xpinstall
-LIBRARY_NAME	= xpinstall
-
-EXPORT_LIBRARY = 1
-IS_COMPONENT	= 1
-MODULE_NAME	= nsSoftwareUpdate
-GRE_MODULE	= 1
-LIBXUL_LIBRARY	= 1
-
-
-# XXX shouldn't need to export this
-EXPORTS		= nsXPITriggerInfo.h
-
-CPPSRCS		= \
-		CertReader.cpp \
-		nsInstallTrigger.cpp \
-		nsJSInstallTriggerGlobal.cpp \
-		nsSoftwareUpdate.cpp \
-		nsXPITriggerInfo.cpp \
-		nsXPInstallManager.cpp \
-		nsXPIInstallInfo.cpp \
-		$(NULL)
-
-LOCAL_INCLUDES	= -I$(srcdir)/../public
-
-include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/xpinstall/src/nsInstall.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *   Douglas Turner <dougt@netscape.com>
- *   Jens Bannmann <jens.b@web.de>
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 __NS_INSTALL_H__
-#define __NS_INSTALL_H__
-
-class nsInstall
-{
-    public:
-
-        enum
-        {
-            BAD_PACKAGE_NAME            = -200,
-            UNEXPECTED_ERROR            = -201,
-            ACCESS_DENIED               = -202,
-            EXECUTION_ERROR             = -203,
-            NO_INSTALL_SCRIPT           = -204,
-            NO_CERTIFICATE              = -205,
-            NO_MATCHING_CERTIFICATE     = -206,
-            CANT_READ_ARCHIVE           = -207,
-            INVALID_ARGUMENTS           = -208,
-            ILLEGAL_RELATIVE_PATH       = -209,
-            USER_CANCELLED              = -210,
-            INSTALL_NOT_STARTED         = -211,
-            SILENT_MODE_DENIED          = -212,
-            NO_SUCH_COMPONENT           = -213,
-            DOES_NOT_EXIST              = -214,
-            READ_ONLY                   = -215,
-            IS_DIRECTORY                = -216,
-            NETWORK_FILE_IS_IN_USE      = -217,
-            APPLE_SINGLE_ERR            = -218,
-            INVALID_PATH_ERR            = -219,
-            PATCH_BAD_DIFF              = -220,
-            PATCH_BAD_CHECKSUM_TARGET   = -221,
-            PATCH_BAD_CHECKSUM_RESULT   = -222,
-            UNINSTALL_FAILED            = -223,
-            PACKAGE_FOLDER_NOT_SET      = -224,
-            EXTRACTION_FAILED           = -225,
-            FILENAME_ALREADY_USED       = -226,
-            INSTALL_CANCELLED           = -227,
-            DOWNLOAD_ERROR              = -228,
-            SCRIPT_ERROR                = -229,
-
-            ALREADY_EXISTS              = -230,
-            IS_FILE                     = -231,
-            SOURCE_DOES_NOT_EXIST       = -232,
-            SOURCE_IS_DIRECTORY         = -233,
-            SOURCE_IS_FILE              = -234,
-            INSUFFICIENT_DISK_SPACE     = -235,
-            FILENAME_TOO_LONG           = -236,
-
-            UNABLE_TO_LOCATE_LIB_FUNCTION = -237,
-            UNABLE_TO_LOAD_LIBRARY        = -238,
-
-            CHROME_REGISTRY_ERROR       = -239,
-
-            MALFORMED_INSTALL           = -240,
-
-            KEY_ACCESS_DENIED           = -241,
-            KEY_DOES_NOT_EXIST          = -242,
-            VALUE_DOES_NOT_EXIST        = -243,
-
-            UNSUPPORTED_TYPE            = -244,
-
-            INVALID_SIGNATURE           = -260,
-            INVALID_HASH                = -261,
-            INVALID_HASH_TYPE           = -262,
-
-            OUT_OF_MEMORY               = -299,
-
-            GESTALT_UNKNOWN_ERR         = -5550,
-            GESTALT_INVALID_ARGUMENT    = -5551,
-
-            SUCCESS                     = 0,
-            REBOOT_NEEDED               = 999
-        };
-};
-
-#endif
deleted file mode 100644
--- a/xpinstall/src/nsInstallTrigger.cpp
+++ /dev/null
@@ -1,453 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "nsXPInstallManager.h"
-#include "nsInstallTrigger.h"
-#include "nsIDOMInstallTriggerGlobal.h"
-
-#include "nscore.h"
-#include "nsAutoPtr.h"
-#include "netCore.h"
-#include "nsIFactory.h"
-#include "nsISupports.h"
-#include "nsPIDOMWindow.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIScriptGlobalObjectOwner.h"
-
-#include "nsIPrefBranch.h"
-#include "nsIPrefService.h"
-#include "nsIPermissionManager.h"
-#include "nsIDocShell.h"
-#include "nsNetUtil.h"
-#include "nsIDOMDocument.h"
-#include "nsIDocument.h"
-#include "nsIPrincipal.h"
-#include "nsIObserverService.h"
-#include "nsIPropertyBag2.h"
-
-#include "nsIComponentManager.h"
-#include "nsIServiceManager.h"
-
-#include "nsIContentHandler.h"
-#include "nsIChannel.h"
-#include "nsIURI.h"
-#include "nsXPIInstallInfo.h"
-
-
-nsInstallTrigger::nsInstallTrigger()
-{
-    mScriptObject   = nsnull;
-}
-
-nsInstallTrigger::~nsInstallTrigger()
-{
-}
-
-
-NS_IMPL_THREADSAFE_ISUPPORTS3 (nsInstallTrigger,
-                              nsIScriptObjectOwner,
-                              nsIDOMInstallTriggerGlobal,
-                              nsIContentHandler)
-
-
-NS_IMETHODIMP
-nsInstallTrigger::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
-{
-    NS_PRECONDITION(nsnull != aScriptObject, "null arg");
-    nsresult res = NS_OK;
-
-    if (nsnull == mScriptObject)
-    {
-        res = NS_NewScriptInstallTriggerGlobal(aContext,
-                                               (nsIDOMInstallTriggerGlobal*)this,
-                                               aContext->GetGlobalObject(),
-                                               &mScriptObject);
-    }
-
-    *aScriptObject = mScriptObject;
-    return res;
-}
-
-NS_IMETHODIMP
-nsInstallTrigger::SetScriptObject(void *aScriptObject)
-{
-  mScriptObject = aScriptObject;
-  return NS_OK;
-}
-
-
-
-
-NS_IMETHODIMP
-nsInstallTrigger::HandleContent(const char * aContentType,
-                                nsIInterfaceRequestor* aWindowContext,
-                                nsIRequest* aRequest)
-{
-    nsresult rv = NS_OK;
-    if (!aRequest)
-        return NS_ERROR_NULL_POINTER;
-
-    if (nsCRT::strcasecmp(aContentType, "application/x-xpinstall") != 0)
-    {
-        // We only support content-type application/x-xpinstall
-        return NS_ERROR_WONT_HANDLE_CONTENT;
-    }
-
-    // Save the URI so nsXPInstallManager can re-load it later
-    nsCOMPtr<nsIURI> uri;
-    nsCAutoString    urispec;
-    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
-    if (channel)
-    {
-        rv = channel->GetURI(getter_AddRefs(uri));
-        if (NS_SUCCEEDED(rv) && uri)
-            rv = uri->GetSpec(urispec);
-    }
-    if (NS_FAILED(rv))
-        return rv;
-    if (urispec.IsEmpty())
-        return NS_ERROR_ILLEGAL_VALUE;
-
-
-    // Save the referrer if any, for permission checks
-    NS_NAMED_LITERAL_STRING(referrerProperty, "docshell.internalReferrer");
-    bool useReferrer = false;
-    nsCOMPtr<nsIURI> referringURI;
-    nsCOMPtr<nsIPropertyBag2> channelprops(do_QueryInterface(channel));
-
-    if (channelprops)
-    {
-        // Get the referrer from the channel properties if we can (not all
-        // channels support our internal-referrer property).
-        //
-        // It's possible docshell explicitly set a null referrer in the case
-        // of typed, pasted, or bookmarked URLs and the like. In such a case
-        // we get a success return value with null pointer.
-        //
-        // A null referrer is automatically whitelisted as an explicit user
-        // action (though they'll still get the confirmation dialog). For a
-        // missing referrer we go to our fall-back plan of using the XPI
-        // location for whitelisting purposes.
-        rv = channelprops->GetPropertyAsInterface(referrerProperty,
-                                                  NS_GET_IID(nsIURI),
-                                                  getter_AddRefs(referringURI));
-        if (NS_SUCCEEDED(rv))
-            useReferrer = true;
-    }
-
-    // Cancel the current request. nsXPInstallManager restarts the download
-    // under its control (shared codepath with InstallTrigger)
-    aRequest->Cancel(NS_BINDING_ABORTED);
-
-
-    // Get the global object of the target window for StartSoftwareUpdate
-    nsCOMPtr<nsIScriptGlobalObjectOwner> globalObjectOwner =
-                                         do_QueryInterface(aWindowContext);
-    nsIScriptGlobalObject* globalObject =
-      globalObjectOwner ? globalObjectOwner->GetScriptGlobalObject() : nsnull;
-    if ( !globalObject )
-        return NS_ERROR_INVALID_ARG;
-
-
-    nsCOMPtr<nsIURI> checkuri;
-
-    if ( useReferrer )
-    {
-        // easiest and most common case: base decision on the page that
-        // contained the link
-        //
-        // NOTE: the XPI itself may be from elsewhere; the user can decide if
-        // they trust the actual source when they get the install confirmation
-        // dialog. The decision we're making here is whether the triggering
-        // site is one which is allowed to annoy the user with modal dialogs.
-
-        checkuri = referringURI;
-    }
-    else
-    {
-        // Now we're stumbing in the dark. In the most likely case the user
-        // simply clicked on an FTP link (no referrer) and it's perfectly
-        // sane to use the current window.
-        //
-        // On the other hand the user might be opening a non-http XPI link
-        // in an unrelated existing window (typed in location bar, bookmark,
-        // dragged link ...) in which case the current window is irrelevant.
-        // If we knew it was one of these explicit user actions we'd like to
-        // allow it, but we have no way of knowing that here.
-        //
-        // But there's no way to distinguish the innocent cases from a clever
-        // malicious site. If we used the target window then evil.com could
-        // embed a presumed allowed site (e.g. mozilla.org) in a frame, then
-        // change the location to the XPI and trigger the install. Or evil.com
-        // could do the same thing in a new window (more work to get around
-        // popup blocking, but possible).
-        //
-        // Our choices appear to be block this type of load entirely or to
-        // trust only the install URI. The former is unacceptably restrictive,
-        // the latter allows malicious sites to pester people with modal
-        // dialogs. As long as the trusted sites don't host bad content that's
-        // no worse than an endless stream of alert()s -- already possible.
-        // If the trusted sites don't even have an ftp server then even this
-        // level of annoyance is not possible.
-        //
-        // If a trusted site hosts an install with an exploitable flaw it
-        // might be possible that a malicious site would attempt to trick
-        // people into installing it, hoping to turn around and exploit it.
-        // This is not entirely far-fetched (it's been done with ActiveX
-        // controls) and will require community policing of the default
-        // trusted sites.
-
-        checkuri = uri;
-    }
-
-    nsAutoPtr<nsXPITriggerInfo> trigger(new nsXPITriggerInfo());
-    nsAutoPtr<nsXPITriggerItem> item(new nsXPITriggerItem(0, NS_ConvertUTF8toUTF16(urispec).get(),
-                                                          nsnull));
-    if (trigger && item)
-    {
-        // trigger will own the item now
-        trigger->Add(item.forget());
-        nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(globalObject);
-        nsCOMPtr<nsIXPIInstallInfo> installInfo =
-                              new nsXPIInstallInfo(win, checkuri, trigger, 0);
-        if (installInfo)
-        {
-            // From here trigger is owned by installInfo until passed on to nsXPInstallManager
-            trigger.forget();
-            if (AllowInstall(checkuri))
-            {
-                return StartInstall(installInfo, nsnull);
-            }
-            else
-            {
-                nsCOMPtr<nsIObserverService> os =
-                  mozilla::services::GetObserverService();
-                if (os)
-                    os->NotifyObservers(installInfo,
-                                        "xpinstall-install-blocked",
-                                        nsnull);
-                return NS_ERROR_ABORT;
-            }
-        }
-    }
-    return NS_ERROR_OUT_OF_MEMORY;
-}
-
-
-// updateWhitelist
-//
-// Helper function called by nsInstallTrigger::AllowInstall().
-// Interprets the pref as a comma-delimited list of hosts and adds each one
-// to the permission manager using the given action. Clear pref when done.
-static void updatePermissions( const char* aPref,
-                               PRUint32 aPermission,
-                               nsIPermissionManager* aPermissionManager,
-                               nsIPrefBranch*        aPrefBranch)
-{
-    NS_PRECONDITION(aPref && aPermissionManager && aPrefBranch, "Null arguments!");
-
-    nsXPIDLCString hostlist;
-    nsresult rv = aPrefBranch->GetCharPref( aPref, getter_Copies(hostlist));
-    if (NS_SUCCEEDED(rv) && !hostlist.IsEmpty())
-    {
-        nsCAutoString host;
-        PRInt32 start=0, match=0;
-        nsresult rv;
-        nsCOMPtr<nsIURI> uri;
-
-        do {
-            match = hostlist.FindChar(',', start);
-
-            host = Substring(hostlist, start, match-start);
-            host.CompressWhitespace();
-            host.Insert("http://", 0);
-
-            rv = NS_NewURI(getter_AddRefs(uri), host);
-            if (NS_SUCCEEDED(rv))
-            {
-                aPermissionManager->Add( uri, XPI_PERMISSION, aPermission, 
-                                         nsIPermissionManager::EXPIRE_NEVER, 0 );
-            }
-            start = match+1;
-        } while ( match > 0 );
-
-        // save empty list, we don't need to do this again
-        aPrefBranch->SetCharPref( aPref, "");
-    }
-}
-
-
-// Check whether an Install is allowed. The launching URI can be null,
-// in which case only the global pref-setting matters.
-bool
-nsInstallTrigger::AllowInstall(nsIURI* aLaunchURI)
-{
-    // Check the global setting.
-    bool xpiEnabled = false;
-    nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
-    if ( !prefBranch)
-    {
-        return true; // no pref service in native install, it's OK
-    }
-
-    prefBranch->GetBoolPref( XPINSTALL_ENABLE_PREF, &xpiEnabled);
-    if ( !xpiEnabled )
-    {
-        // globally turned off
-        return false;
-    }
-
-
-    // Check permissions for the launching host if we have one
-    nsCOMPtr<nsIPermissionManager> permissionMgr =
-                            do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-
-    if ( permissionMgr && aLaunchURI )
-    {
-        bool isChrome = false;
-        bool isFile = false;
-        aLaunchURI->SchemeIs( "chrome", &isChrome );
-        aLaunchURI->SchemeIs( "file", &isFile );
-
-        // file: and chrome: don't need whitelisted hosts
-        if ( !isChrome && !isFile )
-        {
-            // check prefs for permission updates before testing URI
-            updatePermissions( XPINSTALL_WHITELIST_ADD,
-                               nsIPermissionManager::ALLOW_ACTION,
-                               permissionMgr, prefBranch );
-            updatePermissions( XPINSTALL_WHITELIST_ADD_36,
-                               nsIPermissionManager::ALLOW_ACTION,
-                               permissionMgr, prefBranch );
-            updatePermissions( XPINSTALL_BLACKLIST_ADD,
-                               nsIPermissionManager::DENY_ACTION,
-                               permissionMgr, prefBranch );
-
-            bool requireWhitelist = true;
-            prefBranch->GetBoolPref( XPINSTALL_WHITELIST_REQUIRED, &requireWhitelist );
-
-            PRUint32 permission = nsIPermissionManager::UNKNOWN_ACTION;
-            permissionMgr->TestPermission( aLaunchURI, XPI_PERMISSION, &permission );
-
-            if ( permission == nsIPermissionManager::DENY_ACTION )
-            {
-                xpiEnabled = false;
-            }
-            else if ( requireWhitelist &&
-                      permission != nsIPermissionManager::ALLOW_ACTION )
-            {
-                xpiEnabled = false;
-            }
-        }
-    }
-
-    return xpiEnabled;
-}
-
-
-NS_IMETHODIMP
-nsInstallTrigger::GetOriginatingURI(nsIScriptGlobalObject* aGlobalObject, nsIURI * *aUri)
-{
-    NS_ENSURE_ARG_POINTER(aGlobalObject);
-
-    *aUri = nsnull;
-
-    // find the current site
-    nsCOMPtr<nsIDOMDocument> domdoc;
-    nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(aGlobalObject));
-    if ( window )
-    {
-        window->GetDocument(getter_AddRefs(domdoc));
-        nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
-        if ( doc )
-            NS_ADDREF(*aUri = doc->GetDocumentURI());
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsInstallTrigger::UpdateEnabled(nsIScriptGlobalObject* aGlobalObject, bool aUseWhitelist, bool* aReturn)
-{
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = GetOriginatingURI(aGlobalObject, getter_AddRefs(uri));
-    NS_ENSURE_SUCCESS(rv, rv);
-    return UpdateEnabled(uri, aUseWhitelist, aReturn);
-}
-
-NS_IMETHODIMP
-nsInstallTrigger::UpdateEnabled(nsIURI* aURI, bool aUseWhitelist, bool* aReturn)
-{
-    // disallow unless we successfully find otherwise
-    *aReturn = false;
-
-    if (!aUseWhitelist)
-    {
-        // simple global pref check
-        nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
-        if (prefBranch)
-            prefBranch->GetBoolPref( XPINSTALL_ENABLE_PREF, aReturn);
-    }
-    else if (aURI)
-    {
-        *aReturn = AllowInstall(aURI);
-    }
-
-    return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsInstallTrigger::StartInstall(nsIXPIInstallInfo* aInstallInfo, bool* aReturn)
-{
-    if (aReturn)
-        *aReturn = false;
-
-    nsXPInstallManager *mgr = new nsXPInstallManager();
-    if (mgr)
-    {
-        nsresult rv = mgr->InitManagerWithInstallInfo(aInstallInfo);
-        if (NS_SUCCEEDED(rv) && aReturn)
-            *aReturn = true;
-        return rv;
-    }
-    else
-    {
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-}
deleted file mode 100644
--- a/xpinstall/src/nsInstallTrigger.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef __NS_INSTALLTRIGGER_H__
-#define __NS_INSTALLTRIGGER_H__
-
-#include "nscore.h"
-#include "nsString.h"
-#include "nsIFactory.h"
-#include "nsISupports.h"
-#include "nsIScriptObjectOwner.h"
-
-#include "prtypes.h"
-#include "nsHashtable.h"
-
-#include "nsIDOMInstallTriggerGlobal.h"
-#include "nsXPITriggerInfo.h"
-
-#include "nsIContentHandler.h"
-
-#define NOT_CHROME          0
-#define CHROME_SKIN         1
-#define CHROME_LOCALE       2
-#define CHROME_SAFEMAX      CHROME_SKIN
-#define CHROME_CONTENT      4
-#define CHROME_ALL          (CHROME_SKIN | CHROME_LOCALE | CHROME_CONTENT)
-#define CHROME_PROFILE      8
-#define CHROME_DELAYED      0x10
-#define CHROME_SELECT       0x20
-
-#define XPI_PERMISSION      "install"
-
-#define XPI_WHITELIST       true
-#define XPI_GLOBAL          false
-
-#define XPINSTALL_ENABLE_PREF            "xpinstall.enabled"
-#define XPINSTALL_WHITELIST_ADD          "xpinstall.whitelist.add"
-#define XPINSTALL_WHITELIST_ADD_36       "xpinstall.whitelist.add.36"
-#define XPINSTALL_WHITELIST_REQUIRED     "xpinstall.whitelist.required"
-#define XPINSTALL_BLACKLIST_ADD          "xpinstall.blacklist.add"
-
-class nsInstallTrigger: public nsIScriptObjectOwner,
-                        public nsIDOMInstallTriggerGlobal,
-                        public nsIContentHandler
-{
-    public:
-        nsInstallTrigger();
-        virtual ~nsInstallTrigger();
-
-        NS_DECL_ISUPPORTS
-        NS_DECL_NSICONTENTHANDLER
-
-        NS_IMETHOD    GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
-        NS_IMETHOD    SetScriptObject(void* aScriptObject);
-
-        NS_IMETHOD    GetOriginatingURI(nsIScriptGlobalObject* aGlobalObject, nsIURI * *aUri);
-        NS_IMETHOD    UpdateEnabled(nsIScriptGlobalObject* aGlobalObject, bool aUseWhitelist, bool* aReturn);
-        NS_IMETHOD    UpdateEnabled(nsIURI* aURI, bool aUseWhitelist, bool* aReturn);
-        NS_IMETHOD    StartInstall(nsIXPIInstallInfo* aInstallInfo, bool* aReturn);
-
-
-    private:
-        bool    AllowInstall(nsIURI* aLaunchURI);
-        void *mScriptObject;
-};
-
-#define NS_INSTALLTRIGGERCOMPONENT_CONTRACTID "@mozilla.org/xpinstall/installtrigger;1"
-#endif
deleted file mode 100644
--- a/xpinstall/src/nsJSInstallTriggerGlobal.cpp
+++ /dev/null
@@ -1,834 +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) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "jsapi.h"
-#include "nscore.h"
-#include "nsAutoPtr.h"
-#include "nsIScriptContext.h"
-#include "nsIScriptObjectOwner.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsCRT.h"
-#include "nsString.h"
-#include "nsIDOMInstallTriggerGlobal.h"
-#include "nsPIDOMWindow.h"
-#include "nsIDOMDocument.h"
-#include "nsIDocument.h"
-#include "nsIDocShell.h"
-#include "nsIObserverService.h"
-#include "nsInstallTrigger.h"
-#include "nsXPITriggerInfo.h"
-#include "nsDOMJSUtils.h"
-#include "nsXPIInstallInfo.h"
-
-#include "nsIComponentManager.h"
-#include "nsNetUtil.h"
-#include "nsIScriptSecurityManager.h"
-
-#include "nsSoftwareUpdateIIDs.h"
-
-void ConvertJSValToStr(nsString&  aString,
-                      JSContext* aContext,
-                      jsval      aValue)
-{
-  JSString *jsstring;
-
-  if ( !JSVAL_IS_NULL(aValue) &&
-       (jsstring = JS_ValueToString(aContext, aValue)) != nsnull)
-  {
-    aString.Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstring)));
-  }
-  else
-  {
-    aString.Truncate();
-  }
-}
-
-static void
-FinalizeInstallTriggerGlobal(JSContext *cx, JSObject *obj);
-
-/***********************************************************************/
-//
-// class for InstallTriggerGlobal
-//
-JSClass InstallTriggerGlobalClass = {
-  "InstallTrigger",
-  JSCLASS_HAS_PRIVATE,
-  JS_PropertyStub,
-  JS_PropertyStub,
-  JS_PropertyStub,
-  JS_PropertyStub,
-  JS_EnumerateStub,
-  JS_ResolveStub,
-  JS_ConvertStub,
-  FinalizeInstallTriggerGlobal
-};
-
-
-//
-// InstallTriggerGlobal finalizer
-//
-static void
-FinalizeInstallTriggerGlobal(JSContext *cx, JSObject *obj)
-{
-  nsISupports *nativeThis = (nsISupports*)JS_GetPrivate(cx, obj);
-
-  if (nsnull != nativeThis) {
-    // get the js object
-    nsIScriptObjectOwner *owner = nsnull;
-    if (NS_OK == nativeThis->QueryInterface(NS_GET_IID(nsIScriptObjectOwner),
-                                            (void**)&owner)) {
-      owner->SetScriptObject(nsnull);
-      NS_RELEASE(owner);
-    }
-
-    // The addref was part of JSObject construction
-    NS_RELEASE(nativeThis);
-  }
-}
-
-static JSBool CreateNativeObject(JSContext *cx, JSObject *obj, nsIDOMInstallTriggerGlobal **aResult)
-{
-    nsresult result;
-    nsIScriptObjectOwner *owner = nsnull;
-    nsIDOMInstallTriggerGlobal *nativeThis;
-
-    static NS_DEFINE_CID(kInstallTrigger_CID,
-                         NS_SoftwareUpdateInstallTrigger_CID);
-
-    result = CallCreateInstance(kInstallTrigger_CID, &nativeThis);
-    if (NS_FAILED(result)) return JS_FALSE;
-
-    result = nativeThis->QueryInterface(NS_GET_IID(nsIScriptObjectOwner),
-                                        (void **)&owner);
-
-    if (NS_OK != result)
-    {
-        NS_RELEASE(nativeThis);
-        return JS_FALSE;
-    }
-
-    owner->SetScriptObject((void *)obj);
-    JS_SetPrivate(cx, obj, nativeThis);
-
-    *aResult = nativeThis;
-
-    NS_RELEASE(nativeThis);  // we only want one refcnt. JSUtils cleans us up.
-    return JS_TRUE;
-}
-
-//
-// Helper function for URI verification
-//
-static nsresult
-InstallTriggerCheckLoadURIFromScript(JSContext *cx, const nsAString& uriStr)
-{
-    nsresult rv;
-    nsCOMPtr<nsIScriptSecurityManager> secman(
-        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,&rv));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // get the script principal
-    nsCOMPtr<nsIPrincipal> principal;
-    rv = secman->GetSubjectPrincipal(getter_AddRefs(principal));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!principal)
-        return NS_ERROR_FAILURE;
-
-    // convert the requested URL string to a URI
-    // Note that we use a null base URI here, since that's what we use when we
-    // actually convert the string into a URI to load.
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), uriStr);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // are we allowed to load this one?
-    rv = secman->CheckLoadURIWithPrincipal(principal, uri,
-                    nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL);
-    return rv;
-}
-
-//
-// Helper function to get native object
-//
-// This is our own version of JS_GetInstancePrivate() that in addition
-// performs the delayed creation of the native InstallTrigger if necessary
-//
-static nsIDOMInstallTriggerGlobal* getTriggerNative(JSContext *cx, JSObject *obj)
-{
-  if (!JS_InstanceOf(cx, obj, &InstallTriggerGlobalClass, nsnull))
-    return nsnull;
-
-  nsIDOMInstallTriggerGlobal *native = (nsIDOMInstallTriggerGlobal*)JS_GetPrivate(cx, obj);
-  if (!native) {
-    // xpinstall script contexts delay creation of the native.
-    CreateNativeObject(cx, obj, &native);
-  }
-  return native;
-}
-
-//
-// Native method UpdateEnabled
-//
-static JSBool
-InstallTriggerGlobalUpdateEnabled(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
-  nsIDOMInstallTriggerGlobal *nativeThis = getTriggerNative(cx, obj);
-  if (!nativeThis)
-    return JS_FALSE;
-
-  *rval = JSVAL_FALSE;
-
-  nsIScriptGlobalObject *globalObject = nsnull;
-  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
-  if (scriptContext)
-    globalObject = scriptContext->GetGlobalObject();
-
-  bool nativeRet = false;
-  if (globalObject)
-    nativeThis->UpdateEnabled(globalObject, XPI_GLOBAL, &nativeRet);
-
-  *rval = BOOLEAN_TO_JSVAL(nativeRet);
-  return JS_TRUE;
-}
-
-
-//
-// Native method Install
-//
-static JSBool
-InstallTriggerGlobalInstall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
-  nsIDOMInstallTriggerGlobal *nativeThis = getTriggerNative(cx, obj);
-  if (!nativeThis)
-    return JS_FALSE;
-
-  *rval = JSVAL_FALSE;
-
-  // make sure XPInstall is enabled, return false if not
-  nsIScriptGlobalObject *globalObject = nsnull;
-  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
-  if (scriptContext)
-    globalObject = scriptContext->GetGlobalObject();
-
-  if (!globalObject)
-      return JS_TRUE;
-
-  nsCOMPtr<nsIScriptSecurityManager> secman(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
-  if (!secman)
-  {
-    JS_ReportError(cx, "Could not the script security manager service.");
-    return JS_FALSE;
-  }
-  // get the principal.  if it doesn't exist, die.
-  nsCOMPtr<nsIPrincipal> principal;
-  nsresult rv = secman->GetSubjectPrincipal(getter_AddRefs(principal));
-
-  if (NS_FAILED(rv) || !principal)
-  {
-    JS_ReportError(cx, "Could not get the Subject Principal during InstallTrigger.Install()");
-    return JS_FALSE;
-  }
-
-  // get window.location to construct relative URLs
-  nsCOMPtr<nsIURI> baseURL;
-  JSObject* global = JS_GetGlobalObject(cx);
-  if (global)
-  {
-    jsval v;
-    if (JS_GetProperty(cx,global,"location",&v))
-    {
-      nsAutoString location;
-      ConvertJSValToStr( location, cx, v );
-      NS_NewURI(getter_AddRefs(baseURL), location);
-    }
-  }
-
-  bool abortLoad = false;
-
-  // parse associative array of installs
-  if ( argc >= 1 && JSVAL_IS_OBJECT(argv[0]) && JSVAL_TO_OBJECT(argv[0]) )
-  {
-    nsXPITriggerInfo *trigger = new nsXPITriggerInfo();
-    if (!trigger)
-      return JS_FALSE;
-
-    trigger->SetPrincipal(principal);
-
-    JSIdArray *ida = JS_Enumerate( cx, JSVAL_TO_OBJECT(argv[0]) );
-    if ( ida )
-    {
-      jsval v;
-      const PRUnichar *name, *URL;
-      const PRUnichar *iconURL = nsnull;
-
-      for (int i = 0; i < ida->length && !abortLoad; i++ )
-      {
-        JS_IdToValue( cx, ida->vector[i], &v );
-        JSString * str = JS_ValueToString( cx, v );
-        if (!str)
-        {
-          abortLoad = true;
-          break;
-        }
-
-        name = reinterpret_cast<const PRUnichar*>(JS_GetStringChars( str ));
-
-        URL = iconURL = nsnull;
-        JSAutoByteString hash;
-        JS_GetUCProperty( cx, JSVAL_TO_OBJECT(argv[0]), reinterpret_cast<const jschar*>(name), nsCRT::strlen(name), &v );
-        if ( JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) )
-        {
-          jsval v2;
-          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "URL", &v2 ) && !JSVAL_IS_VOID(v2)) {
-            JSString *str = JS_ValueToString(cx, v2);
-            if (!str) {
-              abortLoad = true;
-              break;
-            }
-            URL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
-          }
-
-          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "IconURL", &v2 ) && !JSVAL_IS_VOID(v2)) {
-            JSString *str = JS_ValueToString(cx, v2);
-            if (!str) {
-              abortLoad = true;
-              break;
-            }
-            iconURL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
-          }
-
-          if (JS_GetProperty( cx, JSVAL_TO_OBJECT(v), "Hash", &v2) && !JSVAL_IS_VOID(v2)) {
-            JSString *str = JS_ValueToString(cx, v2);
-            if (!str || !hash.encode(cx, str)) {
-              abortLoad = true;
-              break;
-            }
-          }
-        }
-        else
-        {
-          JSString *str = JS_ValueToString(cx, v);
-          if (!str) {
-            abortLoad = true;
-            break;
-          }
-          URL = reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str));
-        }
-
-        if ( URL )
-        {
-            // Get relative URL to load
-            nsAutoString xpiURL(URL);
-            if (baseURL)
-            {
-                nsCAutoString resolvedURL;
-                baseURL->Resolve(NS_ConvertUTF16toUTF8(xpiURL), resolvedURL);
-                xpiURL = NS_ConvertUTF8toUTF16(resolvedURL);
-            }
-
-            nsAutoString icon(iconURL);
-            if (iconURL && baseURL)
-            {
-                nsCAutoString resolvedIcon;
-                baseURL->Resolve(NS_ConvertUTF16toUTF8(icon), resolvedIcon);
-                icon = NS_ConvertUTF8toUTF16(resolvedIcon);
-            }
-
-            // Make sure we're allowed to load this URL and the icon URL
-            rv = InstallTriggerCheckLoadURIFromScript(cx, xpiURL);
-            if (NS_FAILED(rv))
-                abortLoad = true;
-
-            if (!abortLoad && iconURL)
-            {
-                rv = InstallTriggerCheckLoadURIFromScript(cx, icon);
-                if (NS_FAILED(rv))
-                    abortLoad = true;
-            }
-
-            if (!abortLoad)
-            {
-                // Add the install item to the trigger collection
-                nsXPITriggerItem *item =
-                    new nsXPITriggerItem( name, xpiURL.get(), icon.get(), hash );
-                if ( item )
-                {
-                    trigger->Add( item );
-                }
-                else
-                    abortLoad = true;
-            }
-        }
-        else
-            abortLoad = true;
-      }
-      JS_DestroyIdArray( cx, ida );
-    }
-
-
-    // pass on only if good stuff found
-    if (!abortLoad && trigger->Size() > 0)
-    {
-        nsCOMPtr<nsIURI> checkuri;
-        rv = nativeThis->GetOriginatingURI(globalObject,
-                                           getter_AddRefs(checkuri));
-        if (NS_SUCCEEDED(rv))
-        {
-            nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(globalObject);
-            nsCOMPtr<nsIXPIInstallInfo> installInfo =
-                new nsXPIInstallInfo(win, checkuri, trigger, 0);
-            if (installInfo)
-            {
-                // installInfo now owns triggers
-                bool enabled = false;
-                nativeThis->UpdateEnabled(checkuri, XPI_WHITELIST, &enabled);
-                if (!enabled)
-                {
-                    nsCOMPtr<nsIObserverService> os =
-                      mozilla::services::GetObserverService();
-                    if (os)
-                        os->NotifyObservers(installInfo,
-                                            "xpinstall-install-blocked",
-                                            nsnull);
-                }
-                else
-                {
-                    // save callback function if any (ignore bad args for now)
-                    if ( argc >= 2 && JS_TypeOfValue(cx,argv[1]) == JSTYPE_FUNCTION )
-                    {
-                        trigger->SaveCallback( cx, argv[1] );
-                    }
-
-                    bool result;
-                    nativeThis->StartInstall(installInfo, &result);
-                    *rval = BOOLEAN_TO_JSVAL(result);
-                }
-                return JS_TRUE;
-            }
-        }
-    }
-    // didn't pass it on so we must delete trigger
-    delete trigger;
-  }
-
-  JS_ReportError(cx, "Incorrect arguments to InstallTrigger.Install()");
-  return JS_FALSE;
-}
-
-
-//
-// Native method InstallChrome
-//
-static JSBool
-InstallTriggerGlobalInstallChrome(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
-  nsIDOMInstallTriggerGlobal *nativeThis = getTriggerNative(cx, obj);
-  if (!nativeThis)
-    return JS_FALSE;
-
-  uint32       chromeType = NOT_CHROME;
-  nsAutoString sourceURL;
-  nsAutoString name;
-
-  *rval = JSVAL_FALSE;
-
-  // get chromeType first, the update enabled check for skins skips whitelisting
-  if (argc >=1)
-  {
-      if (!JS_ValueToECMAUint32(cx, argv[0], &chromeType))
-          return JS_FALSE;
-  }
-
-  // make sure XPInstall is enabled, return if not
-  nsIScriptGlobalObject *globalObject = nsnull;
-  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
-  if (scriptContext)
-      globalObject = scriptContext->GetGlobalObject();
-
-  if (!globalObject)
-      return JS_TRUE;
-
-  // get window.location to construct relative URLs
-  nsCOMPtr<nsIURI> baseURL;
-  JSObject* global = JS_GetGlobalObject(cx);
-  if (global)
-  {
-    jsval v;
-    if (JS_GetProperty(cx,global,"location",&v))
-    {
-      nsAutoString location;
-      ConvertJSValToStr( location, cx, v );
-      NS_NewURI(getter_AddRefs(baseURL), location);
-    }
-  }
-
-
-  if ( argc >= 3 )
-  {
-    ConvertJSValToStr(sourceURL, cx, argv[1]);
-    ConvertJSValToStr(name, cx, argv[2]);
-
-    if (baseURL)
-    {
-        nsCAutoString resolvedURL;
-        baseURL->Resolve(NS_ConvertUTF16toUTF8(sourceURL), resolvedURL);
-        sourceURL = NS_ConvertUTF8toUTF16(resolvedURL);
-    }
-
-    // Make sure caller is allowed to load this url.
-    nsresult rv = InstallTriggerCheckLoadURIFromScript(cx, sourceURL);
-    if (NS_FAILED(rv))
-        return JS_FALSE;
-
-    if ( chromeType & CHROME_ALL )
-    {
-        // there's at least one known chrome type
-        nsCOMPtr<nsIURI> checkuri;
-        nsresult rv = nativeThis->GetOriginatingURI(globalObject,
-                                                    getter_AddRefs(checkuri));
-        if (NS_SUCCEEDED(rv))
-        {
-            nsAutoPtr<nsXPITriggerInfo> trigger(new nsXPITriggerInfo());
-            nsAutoPtr<nsXPITriggerItem> item(new nsXPITriggerItem(name.get(),
-                                                                  sourceURL.get(),
-                                                                  nsnull));
-            if (trigger && item)
-            {
-                // trigger will free item when complete
-                trigger->Add(item.forget());
-                nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(globalObject);
-                nsCOMPtr<nsIXPIInstallInfo> installInfo =
-                    new nsXPIInstallInfo(win, checkuri, trigger, chromeType);
-                if (installInfo)
-                {
-                    // installInfo owns trigger now
-                    trigger.forget();
-                    bool enabled = false;
-                    nativeThis->UpdateEnabled(checkuri, XPI_WHITELIST,
-                                              &enabled);
-                    if (!enabled)
-                    {
-                        nsCOMPtr<nsIObserverService> os =
-                          mozilla::services::GetObserverService();
-                        if (os)
-                            os->NotifyObservers(installInfo,
-                                                "xpinstall-install-blocked",
-                                                nsnull);
-                    }
-                    else
-                    {
-                        bool nativeRet = false;
-                        nativeThis->StartInstall(installInfo, &nativeRet);
-                        *rval = BOOLEAN_TO_JSVAL(nativeRet);
-                    }
-                }
-            }
-        }
-    }
-  }
-  return JS_TRUE;
-}
-
-
-//
-// Native method StartSoftwareUpdate
-//
-static JSBool
-InstallTriggerGlobalStartSoftwareUpdate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
-  nsIDOMInstallTriggerGlobal *nativeThis = getTriggerNative(cx, obj);
-  if (!nativeThis)
-    return JS_FALSE;
-
-  bool         nativeRet;
-  PRInt32      flags = 0;
-
-  *rval = JSVAL_FALSE;
-
-  nsIScriptGlobalObject *globalObject = nsnull;
-  nsIScriptContext *scriptContext = GetScriptContextFromJSContext(cx);
-  if (scriptContext)
-      globalObject = scriptContext->GetGlobalObject();
-
-  if (!globalObject)
-      return JS_TRUE;
-
-  // get window.location to construct relative URLs
-  nsCOMPtr<nsIURI> baseURL;
-  JSObject* global = JS_GetGlobalObject(cx);
-  if (global)
-  {
-    jsval v;
-    if (JS_GetProperty(cx,global,"location",&v))
-    {
-      nsAutoString location;
-      ConvertJSValToStr( location, cx, v );
-      NS_NewURI(getter_AddRefs(baseURL), location);
-    }
-  }
-
-
-  if ( argc >= 1 )
-  {
-    nsAutoString xpiURL;
-    ConvertJSValToStr(xpiURL, cx, argv[0]);
-    if (baseURL)
-    {
-        nsCAutoString resolvedURL;
-        baseURL->Resolve(NS_ConvertUTF16toUTF8(xpiURL), resolvedURL);
-        xpiURL = NS_ConvertUTF8toUTF16(resolvedURL);
-    }
-
-    // Make sure caller is allowed to load this url.
-    nsresult rv = InstallTriggerCheckLoadURIFromScript(cx, xpiURL);
-    if (NS_FAILED(rv))
-        return JS_FALSE;
-
-    if (argc >= 2 && !JS_ValueToInt32(cx, argv[1], (int32 *)&flags))
-    {
-        JS_ReportError(cx, "StartSoftwareUpdate() 2nd parameter must be a number");
-        return JS_FALSE;
-    }
-
-    nsCOMPtr<nsIURI> checkuri;
-    rv = nativeThis->GetOriginatingURI(globalObject, getter_AddRefs(checkuri));
-    if (NS_SUCCEEDED(rv))
-    {
-        nsAutoPtr<nsXPITriggerInfo> trigger(new nsXPITriggerInfo());
-        nsAutoPtr<nsXPITriggerItem> item(new nsXPITriggerItem(0,
-                                                              xpiURL.get(),
-                                                              nsnull));
-        if (trigger && item)
-        {
-            // trigger will free item when complete
-            trigger->Add(item.forget());
-            nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(globalObject);
-            nsCOMPtr<nsIXPIInstallInfo> installInfo =
-                                new nsXPIInstallInfo(win, checkuri, trigger, 0);
-            if (installInfo)
-            {
-                // From here trigger is owned by installInfo until passed on to nsXPInstallManager
-                trigger.forget();
-                bool enabled = false;
-                nativeThis->UpdateEnabled(checkuri, XPI_WHITELIST, &enabled);
-                if (!enabled)
-                {
-                    nsCOMPtr<nsIObserverService> os =
-                      mozilla::services::GetObserverService();
-                    if (os)
-                        os->NotifyObservers(installInfo,
-                                            "xpinstall-install-blocked",
-                                            nsnull);
-                }
-                else
-                {
-                    nativeThis->StartInstall(installInfo, &nativeRet);
-                    *rval = BOOLEAN_TO_JSVAL(nativeRet);
-                }
-            }
-        }
-    }
-  }
-  else
-  {
-    JS_ReportError(cx, "Function StartSoftwareUpdate requires 1 parameters");
-    return JS_FALSE;
-  }
-
-  return JS_TRUE;
-}
-
-
-//
-// InstallTriggerGlobal class methods
-//
-static JSFunctionSpec InstallTriggerGlobalMethods[] =
-{
-  {"UpdateEnabled",         InstallTriggerGlobalUpdateEnabled,         0,0,0},
-  {"StartSoftwareUpdate",   InstallTriggerGlobalStartSoftwareUpdate,   2,0,0},
-  {"updateEnabled",         InstallTriggerGlobalUpdateEnabled,         0,0,0},
-  {"enabled",               InstallTriggerGlobalUpdateEnabled,         0,0,0},
-  {"install",               InstallTriggerGlobalInstall,               2,0,0},
-  {"installChrome",         InstallTriggerGlobalInstallChrome,         2,0,0},
-  {"startSoftwareUpdate",   InstallTriggerGlobalStartSoftwareUpdate,   2,0,0},
-  {nsnull,nsnull,0,0,0}
-};
-
-
-static JSConstDoubleSpec diff_constants[] =
-{
-    { nsIDOMInstallTriggerGlobal::MAJOR_DIFF,    "MAJOR_DIFF" },
-    { nsIDOMInstallTriggerGlobal::MINOR_DIFF,    "MINOR_DIFF" },
-    { nsIDOMInstallTriggerGlobal::REL_DIFF,      "REL_DIFF"   },
-    { nsIDOMInstallTriggerGlobal::BLD_DIFF,      "BLD_DIFF"   },
-    { nsIDOMInstallTriggerGlobal::EQUAL,         "EQUAL"      },
-    { nsIDOMInstallTriggerGlobal::NOT_FOUND,     "NOT_FOUND"  },
-    { CHROME_SKIN,                               "SKIN"       },
-    { CHROME_LOCALE,                             "LOCALE"     },
-    { CHROME_CONTENT,                            "CONTENT"    },
-    { CHROME_ALL,                                "PACKAGE"    },
-    {0,nsnull}
-};
-
-
-
-nsresult InitInstallTriggerGlobalClass(JSContext *jscontext, JSObject *global, void** prototype)
-{
-  JSObject *proto = nsnull;
-
-  if (prototype != nsnull)
-    *prototype = nsnull;
-
-    proto = JS_InitClass(jscontext,                       // context
-                         global,                          // global object
-                         nsnull,                          // parent proto
-                         &InstallTriggerGlobalClass,      // JSClass
-                         nsnull,                          // JSNative ctor
-                         nsnull,                          // ctor args
-                         nsnull,                          // proto props
-                         nsnull,                          // proto funcs
-                         nsnull,                          // ctor props (static)
-                         InstallTriggerGlobalMethods);    // ctor funcs (static)
-
-
-    if (nsnull == proto) return NS_ERROR_FAILURE;
-
-    if ( false == JS_DefineConstDoubles(jscontext, proto, diff_constants) )
-            return NS_ERROR_FAILURE;
-
-    if (prototype != nsnull)
-      *prototype = proto;
-
-  return NS_OK;
-}
-
-
-
-//
-// InstallTriggerGlobal class initialization
-//
-nsresult NS_InitInstallTriggerGlobalClass(nsIScriptContext *aContext, void **aPrototype)
-{
-  JSContext *jscontext = aContext->GetNativeContext();
-  JSObject *proto = nsnull;
-  JSObject *constructor = nsnull;
-  JSObject *global = JS_GetGlobalObject(jscontext);
-  jsval vp;
-
-  if ((true != JS_LookupProperty(jscontext, global, "InstallTriggerGlobal", &vp)) ||
-      !JSVAL_IS_OBJECT(vp) ||
-      ((constructor = JSVAL_TO_OBJECT(vp)) == nsnull) ||
-      (true != JS_LookupProperty(jscontext, JSVAL_TO_OBJECT(vp), "prototype", &vp)) ||
-      !JSVAL_IS_OBJECT(vp))
-  {
-    nsresult rv = InitInstallTriggerGlobalClass(jscontext, global, (void**)&proto);
-    if (NS_FAILED(rv)) return rv;
-  }
-  else if ((nsnull != constructor) && JSVAL_IS_OBJECT(vp))
-  {
-    proto = JSVAL_TO_OBJECT(vp);
-  }
-  else
-  {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (aPrototype)
-    *aPrototype = proto;
-
-  return NS_OK;
-}
-
-
-//
-// Method for creating a new InstallTriggerGlobal JavaScript object
-//
-nsresult
-NS_NewScriptInstallTriggerGlobal(nsIScriptContext *aContext,
-                                 nsISupports *aSupports, nsISupports *aParent,
-                                 void **aReturn)
-{
-  NS_PRECONDITION(nsnull != aContext && nsnull != aSupports &&
-                  nsnull != aReturn,
-                  "null argument to NS_NewScriptInstallTriggerGlobal");
-
-  JSObject *proto;
-  JSObject *parent = nsnull;
-  JSContext *jscontext = aContext->GetNativeContext();
-  nsresult result = NS_OK;
-  nsIDOMInstallTriggerGlobal *installTriggerGlobal;
-
-  nsCOMPtr<nsIScriptObjectOwner> owner(do_QueryInterface(aParent));
-
-  if (owner) {
-    if (NS_OK != owner->GetScriptObject(aContext, (void **)&parent)) {
-      return NS_ERROR_FAILURE;
-    }
-  } else {
-    nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aParent));
-
-    if (sgo) {
-      parent = sgo->GetGlobalJSObject();
-    } else {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  if (NS_OK != NS_InitInstallTriggerGlobalClass(aContext, (void **)&proto)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  result = CallQueryInterface(aSupports, &installTriggerGlobal);
-  if (NS_OK != result) {
-    return result;
-  }
-
-  // create a js object for this class
-  *aReturn = JS_NewObject(jscontext, &InstallTriggerGlobalClass, proto, parent);
-  if (nsnull != *aReturn) {
-    // connect the native object to the js object
-    JS_SetPrivate(jscontext, (JSObject *)*aReturn, installTriggerGlobal);
-  }
-  else {
-    NS_RELEASE(installTriggerGlobal);
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
deleted file mode 100644
--- a/xpinstall/src/nsSoftwareUpdate.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "nscore.h"
-#include "nsIGenericFactory.h"
-#include "nsIFactory.h"
-#include "nsISupports.h"
-#include "nsIComponentManager.h"
-#include "nsIComponentRegistrar.h"
-#include "nsIServiceManager.h"
-#include "nsICategoryManager.h"
-#include "nsIScriptNameSpaceManager.h"
-#include "nsCURILoader.h"
-
-#include "nsSoftwareUpdateIIDs.h"
-
-#include "nsInstallTrigger.h"
-#include "nsXPInstallManager.h"
-
-//----------------------------------------------------------------------
-
-// Functions used to create new instances of a given object by the
-// generic factory.
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsInstallTrigger)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsXPInstallManager)
-
-//----------------------------------------------------------------------
-
-static NS_METHOD
-RegisterInstallTrigger( nsIComponentManager *aCompMgr,
-                        nsIFile *aPath,
-                        const char *registryLocation,
-                        const char *componentType,
-                        const nsModuleComponentInfo *info)
-{
-  nsresult rv = NS_OK;
-
-  nsCOMPtr<nsICategoryManager> catman =
-    do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsXPIDLCString previous;
-  rv = catman->AddCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
-                                "InstallTrigger",
-                                NS_INSTALLTRIGGERCOMPONENT_CONTRACTID,
-                                true, true, getter_Copies(previous));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-
-// The list of components we register
-static const nsModuleComponentInfo components[] =
-{
-    { "InstallTrigger Component",
-       NS_SoftwareUpdateInstallTrigger_CID,
-       NS_INSTALLTRIGGERCOMPONENT_CONTRACTID,
-       nsInstallTriggerConstructor,
-       RegisterInstallTrigger
-    },
-
-    { "XPInstall Content Handler",
-      NS_SoftwareUpdateInstallTrigger_CID,
-      NS_CONTENT_HANDLER_CONTRACTID_PREFIX"application/x-xpinstall",
-      nsInstallTriggerConstructor
-    },
-
-    { "XPInstallManager Component",
-      NS_XPInstallManager_CID,
-      NS_XPINSTALLMANAGERCOMPONENT_CONTRACTID,
-      nsXPInstallManagerConstructor
-    }
-};
-
-NS_IMPL_NSGETMODULE(nsSoftwareUpdate, components)
deleted file mode 100644
--- a/xpinstall/src/nsXPIInstallInfo.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ***** 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 XPInstall.
- *
- * The Initial Developer of the Original Code is
- * Dave Townsend <dtownsend@oxymoronical.com>.
- *
- * Portions created by the Initial Developer are Copyright (C) 2007
- * 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 *****
- */
-
-#include "nsXPIInstallInfo.h"
-
-NS_IMPL_ISUPPORTS1(nsXPIInstallInfo, nsIXPIInstallInfo)
-
-nsXPIInstallInfo::nsXPIInstallInfo(nsIDOMWindow *aOriginatingWindow,
-                                   nsIURI *aOriginatingURI,
-                                   nsXPITriggerInfo *aTriggerInfo,
-                                   PRUint32 aChromeType)
-  : mOriginatingWindow(aOriginatingWindow), mOriginatingURI(aOriginatingURI),
-    mTriggerInfo(aTriggerInfo), mChromeType(aChromeType)
-{
-}
-
-nsXPIInstallInfo::~nsXPIInstallInfo()
-{
-    delete mTriggerInfo;
-}
-
-/* [noscript, notxpcom] attribute triggerInfoPtr triggerInfo; */
-NS_IMETHODIMP
-nsXPIInstallInfo::GetTriggerInfo(nsXPITriggerInfo * *aTriggerInfo)
-{
-    *aTriggerInfo = mTriggerInfo;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsXPIInstallInfo::SetTriggerInfo(nsXPITriggerInfo * aTriggerInfo)
-{
-    mTriggerInfo = aTriggerInfo;
-    return NS_OK;
-}
-
-/* readonly attribute nsIDOMWindow originatingWindow; */
-NS_IMETHODIMP
-nsXPIInstallInfo::GetOriginatingWindow(nsIDOMWindow * *aOriginatingWindow)
-{
-    NS_IF_ADDREF(*aOriginatingWindow = mOriginatingWindow);
-    return NS_OK;
-}
-
-/* readonly attribute nsIURI uri; */
-NS_IMETHODIMP
-nsXPIInstallInfo::GetOriginatingURI(nsIURI * *aOriginatingURI)
-{
-    NS_IF_ADDREF(*aOriginatingURI = mOriginatingURI);
-    return NS_OK;
-}
-
-/* readonly attribute PRUint32 type; */
-NS_IMETHODIMP
-nsXPIInstallInfo::GetChromeType(PRUint32 *aChromeType)
-{
-    *aChromeType = mChromeType;
-    return NS_OK;
-}
deleted file mode 100644
--- a/xpinstall/src/nsXPIInstallInfo.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* ***** 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 XPInstall.
- *
- * The Initial Developer of the Original Code is
- * Dave Townsend <dtownsend@oxymoronical.com>.
- *
- * Portions created by the Initial Developer are Copyright (C) 2007
- * 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 *****
- */
-
-#include "nsXPITriggerInfo.h"
-#include "nsIXPIInstallInfo.h"
-#include "nsIDOMWindow.h"
-#include "nsIDocShell.h"
-#include "nsIURI.h"
-
-class nsXPIInstallInfo : public nsIXPIInstallInfo
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIXPIINSTALLINFO
-
-  nsXPIInstallInfo(nsIDOMWindow *aOriginatingWindow,
-                   nsIURI *aOriginatingURI, nsXPITriggerInfo *aTriggerInfo,
-                   PRUint32 aChromeType);
-
-private:
-  ~nsXPIInstallInfo();
-
-  nsCOMPtr<nsIDOMWindow> mOriginatingWindow;
-  nsCOMPtr<nsIURI> mOriginatingURI;
-  nsXPITriggerInfo* mTriggerInfo;
-  PRUint32 mChromeType;
-};
deleted file mode 100644
--- a/xpinstall/src/nsXPITriggerInfo.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "jscntxt.h"
-#include "nscore.h"
-#include "plstr.h"
-#include "nsXPITriggerInfo.h"
-#include "nsNetUtil.h"
-#include "nsDebug.h"
-#include "nsAutoPtr.h"
-#include "nsThreadUtils.h"
-#include "nsIServiceManager.h"
-#include "nsIJSContextStack.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsICryptoHash.h"
-#include "nsIX509Cert.h"
-
-using namespace mozilla;
-
-//
-// nsXPITriggerItem
-//
-
-nsXPITriggerItem::nsXPITriggerItem( const PRUnichar* aName,
-                                    const PRUnichar* aURL,
-                                    const PRUnichar* aIconURL,
-                                    const char* aHash,
-                                    PRInt32 aFlags)
-    : mName(aName), mURL(aURL), mIconURL(aIconURL), mHashFound(false), mFlags(aFlags)
-{
-    MOZ_COUNT_CTOR(nsXPITriggerItem);
-
-    // check for arguments
-    PRInt32 qmark = mURL.FindChar('?');
-    if ( qmark != kNotFound )
-    {
-        mArguments = Substring( mURL, qmark+1, mURL.Length() );
-    }
-
-    // construct name if not passed in
-    if ( mName.IsEmpty() )
-    {
-        // Use the filename as the display name by starting after the last
-        // slash in the URL, looking backwards from the arguments delimiter if
-        // we found one. By good fortune using kNotFound as the offset for
-        // RFindChar() starts at the end, so we can use qmark in all cases.
-
-        PRInt32 namestart = mURL.RFindChar( '/', qmark );
-
-        // the real start is after the slash (or 0 if not found)
-        namestart = ( namestart==kNotFound ) ? 0 : namestart + 1;
-
-        PRInt32 length;
-        if (qmark == kNotFound)
-            length =  mURL.Length();      // no '?', slurp up rest of URL
-        else
-            length = (qmark - namestart); // filename stops at the '?'
-
-        mName = Substring( mURL, namestart, length );
-    }
-
-    // parse optional hash into its parts
-    if (aHash)
-    {
-        mHashFound = true;
-
-        char * colon = PL_strchr(aHash, ':');
-        if (colon)
-        {
-            mHasher = do_CreateInstance("@mozilla.org/security/hash;1");
-            if (!mHasher) return;
-
-            *colon = '\0'; // null the colon so that aHash is just the type.
-            nsresult rv = mHasher->InitWithString(nsDependentCString(aHash));
-            *colon = ':';  // restore the colon
-
-            if (NS_SUCCEEDED(rv))
-                mHash = colon+1;
-        }
-    }
-}
-
-nsXPITriggerItem::~nsXPITriggerItem()
-{
-    MOZ_COUNT_DTOR(nsXPITriggerItem);
-}
-
-const PRUnichar*
-nsXPITriggerItem::GetSafeURLString()
-{
-    // create the safe url string the first time
-    if (mSafeURL.IsEmpty() && !mURL.IsEmpty())
-    {
-        nsCOMPtr<nsIURI> uri;
-        NS_NewURI(getter_AddRefs(uri), mURL);
-        if (uri)
-        {
-            nsCAutoString spec;
-            uri->SetUserPass(EmptyCString());
-            uri->GetSpec(spec);
-            mSafeURL = NS_ConvertUTF8toUTF16(spec);
-        }
-    }
-
-    return mSafeURL.get();
-}
-
-void
-nsXPITriggerItem::SetPrincipal(nsIPrincipal* aPrincipal)
-{
-    mPrincipal = aPrincipal;
-
-    // aPrincipal can be null for various failure cases.
-    // see bug 213894 for an example.
-    // nsXPInstallManager::OnCertAvailable can be called with a null principal
-    // and it can also force a null principal.
-    if (!aPrincipal)
-        return;
-
-    bool hasCert;
-    aPrincipal->GetHasCertificate(&hasCert);
-    if (hasCert) {
-        nsCOMPtr<nsISupports> certificate;
-        aPrincipal->GetCertificate(getter_AddRefs(certificate));
-
-        nsCOMPtr<nsIX509Cert> x509 = do_QueryInterface(certificate);
-        if (x509) {
-            x509->GetCommonName(mCertName);
-            if (mCertName.Length() > 0)
-                return;
-        }
-
-        nsCAutoString prettyName;
-        aPrincipal->GetPrettyName(prettyName);
-        CopyUTF8toUTF16(prettyName, mCertName);
-    }
-}
-
-//
-// nsXPITriggerInfo
-//
-
-nsXPITriggerInfo::nsXPITriggerInfo()
-  : mCx(0), mCbval(JSVAL_NULL)
-{
-    MOZ_COUNT_CTOR(nsXPITriggerInfo);
-}
-
-nsXPITriggerInfo::~nsXPITriggerInfo()
-{
-    nsXPITriggerItem* item;
-
-    for(PRUint32 i=0; i < Size(); i++)
-    {
-        item = Get(i);
-        delete item;
-    }
-    mItems.Clear();
-
-    if ( mCx && !JSVAL_IS_NULL(mCbval) ) {
-        JS_BeginRequest(mCx);
-        JS_RemoveValueRoot(mCx, &mCbval );
-        JS_EndRequest(mCx);
-    }
-
-    MOZ_COUNT_DTOR(nsXPITriggerInfo);
-}
-
-void nsXPITriggerInfo::SaveCallback( JSContext *aCx, jsval aVal )
-{
-    NS_ASSERTION( mCx == 0, "callback set twice, memory leak" );
-    // We'll only retain the callback if we can get a strong reference to the
-    // context.
-    if (!(JS_GetOptions(aCx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
-        return;
-    mContextWrapper = static_cast<nsISupports *>(JS_GetContextPrivate(aCx));
-    if (!mContextWrapper)
-        return;
-
-    mCx = aCx;
-    mCbval = aVal;
-    mThread = do_GetCurrentThread();
-
-    if ( !JSVAL_IS_NULL(mCbval) ) {
-        JS_BeginRequest(mCx);
-        JS_AddValueRoot(mCx, &mCbval );
-        JS_EndRequest(mCx);
-    }
-}
-
-XPITriggerEvent::~XPITriggerEvent()
-{
-    JS_BeginRequest(cx);
-    JS_RemoveValueRoot(cx, &cbval);
-    JS_EndRequest(cx);
-}
-
-NS_IMETHODIMP
-XPITriggerEvent::Run()
-{
-    JSAutoRequest ar(cx);
-
-    // If Components doesn't exist in the global object then XPConnect has
-    // been torn down, probably because the page was closed. Bail out if that
-    // is the case.
-    JSObject* innerGlobal = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(cbval));
-    jsval components;
-    if (!JS_LookupProperty(cx, innerGlobal, "Components", &components) ||
-        !JSVAL_IS_OBJECT(components))
-        return 0;
-
-    // Build arguments into rooted jsval array
-    jsval args[2] = { JSVAL_NULL, JSVAL_NULL };
-    js::AutoArrayRooter tvr(cx, ArrayLength(args), args);
-
-    // args[0] is the URL
-    JSString *str = JS_NewUCStringCopyZ(cx, reinterpret_cast<const jschar*>(URL.get()));
-    if (!str)
-        return 0;
-    args[0] = STRING_TO_JSVAL(str);
-
-    // args[1] is the status
-    if (!JS_NewNumberValue(cx, status, &args[1]))
-        return 0;
-
-    class StackPushGuard {
-        nsCOMPtr<nsIJSContextStack> mStack;
-    public:
-        StackPushGuard(JSContext *cx)
-          : mStack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"))
-        {
-            if (mStack)
-                mStack->Push(cx);
-        }
-
-        ~StackPushGuard()
-        {
-            if (mStack)
-                mStack->Pop(nsnull);
-        }
-    } stackPushGuard(cx);
-
-    nsCOMPtr<nsIScriptSecurityManager> secman =
-        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
-    if (!secman)
-    {
-        JS_ReportError(cx, "Could not get script security manager service");
-        return 0;
-    }
-
-    nsCOMPtr<nsIPrincipal> principal;
-    nsresult rv = secman->GetSubjectPrincipal(getter_AddRefs(principal));
-
-    if (NS_FAILED(rv) || !principal)
-    {
-         JS_ReportError(cx, "Could not get principal from script security manager");
-         return 0;
-    }
-
-    bool equals = false;
-    principal->Equals(princ, &equals);
-    if (!equals)
-    {
-        JS_ReportError(cx, "Principal of callback context is different than InstallTriggers");
-        return 0;
-    }
-
-    jsval ret;
-    JS_CallFunctionValue(cx,
-                         JS_GetGlobalObject(cx),
-                         cbval,
-                         2,
-                         args,
-                         &ret);
-    return 0;
-}
-
-
-void nsXPITriggerInfo::SendStatus(const PRUnichar* URL, PRInt32 status)
-{
-    nsresult rv;
-
-    if ( mCx && mContextWrapper && !JSVAL_IS_NULL(mCbval) )
-    {
-        // create event and post it
-        nsRefPtr<XPITriggerEvent> event = new XPITriggerEvent();
-        if (event)
-        {
-            event->URL      = URL;
-            event->status   = status;
-            event->cx       = mCx;
-            event->princ    = mPrincipal;
-
-            event->cbval    = mCbval;
-            JS_BeginRequest(event->cx);
-            JS_AddNamedValueRoot(event->cx, &event->cbval,
-                            "XPITriggerEvent::cbval" );
-            JS_EndRequest(event->cx);
-
-            // Hold a strong reference to keep the underlying
-            // JSContext from dying before we handle this event.
-            event->ref      = mContextWrapper;
-
-            rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL);
-        }
-        else
-            rv = NS_ERROR_OUT_OF_MEMORY;
-
-        if ( NS_FAILED( rv ) )
-        {
-            // couldn't get event queue -- maybe window is gone or
-            // some similarly catastrophic occurrence
-            NS_WARNING("failed to dispatch XPITriggerEvent");
-        }
-    }
-}
deleted file mode 100644
--- a/xpinstall/src/nsXPITriggerInfo.h
+++ /dev/null
@@ -1,144 +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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 nsXPITriggerInfo_h
-#define nsXPITriggerInfo_h
-
-#include "nsString.h"
-#include "nsVoidArray.h"
-#include "nsCOMPtr.h"
-#include "nsISupportsUtils.h"
-#include "nsILocalFile.h"
-#include "nsIOutputStream.h"
-#include "jsapi.h"
-#include "prthread.h"
-#include "nsIXPConnect.h"
-#include "nsICryptoHash.h"
-#include "nsIPrincipal.h"
-#include "nsThreadUtils.h"
-
-struct XPITriggerEvent : public nsRunnable {
-    nsString    URL;
-    PRInt32     status;
-    JSContext*  cx;
-    jsval       cbval;
-    nsCOMPtr<nsISupports> ref;
-    nsCOMPtr<nsIPrincipal> princ;
-
-    virtual ~XPITriggerEvent();
-    NS_IMETHOD Run();
-};
-
-class nsXPITriggerItem
-{
-  public:
-    nsXPITriggerItem( const PRUnichar* name,
-                      const PRUnichar* URL,
-                      const PRUnichar* iconURL,
-                      const char* hash = nsnull,
-                      PRInt32 flags = 0);
-    ~nsXPITriggerItem();
-
-    nsString    mName;
-    nsString    mURL;
-    nsString    mIconURL;
-    nsString    mArguments;
-    nsString    mCertName;
-
-    bool        mHashFound; // this flag indicates that we found _some_ hash info in the trigger
-    nsCString   mHash;
-    nsCOMPtr<nsICryptoHash> mHasher;
-    PRInt32     mFlags;
-
-    nsCOMPtr<nsILocalFile>      mFile;
-    nsCOMPtr<nsIOutputStream>   mOutStream;
-    nsCOMPtr<nsIPrincipal>      mPrincipal;
-
-    void    SetPrincipal(nsIPrincipal* aPrincipal);
-
-    bool    IsFileURL() { return StringBeginsWith(mURL, NS_LITERAL_STRING("file:/")); }
-
-    const PRUnichar* GetSafeURLString();
-
-  private:
-    //-- prevent inadvertent copies and assignments
-    nsXPITriggerItem& operator=(const nsXPITriggerItem& rhs);
-    nsXPITriggerItem(const nsXPITriggerItem& rhs);
-
-    nsString    mSafeURL;
-};
-
-
-
-class nsXPITriggerInfo
-{
-  public:
-    nsXPITriggerInfo();
-    ~nsXPITriggerInfo();
-
-    void                Add( nsXPITriggerItem *aItem )
-                        { if ( aItem ) mItems.AppendElement( (void*)aItem ); }
-
-    nsXPITriggerItem*   Get( PRUint32 aIndex )
-                        { return (nsXPITriggerItem*)mItems.ElementAt(aIndex);}
-
-    void                SaveCallback( JSContext *aCx, jsval aVal );
-
-    PRUint32            Size() { return mItems.Count(); }
-
-    void                SendStatus(const PRUnichar* URL, PRInt32 status);
-
-    void                SetPrincipal(nsIPrincipal* aPrinc) { mPrincipal = aPrinc; }
-
-
-  private:
-    nsVoidArray mItems;
-    JSContext   *mCx;
-    nsCOMPtr<nsISupports> mContextWrapper;
-    jsval       mCbval;
-    nsCOMPtr<nsIThread> mThread;
-
-    nsCOMPtr<nsIPrincipal>      mPrincipal;
-
-    //-- prevent inadvertent copies and assignments
-    nsXPITriggerInfo& operator=(const nsXPITriggerInfo& rhs);
-    nsXPITriggerInfo(const nsXPITriggerInfo& rhs);
-};
-
-#endif /* nsXPITriggerInfo_h */
deleted file mode 100644
--- a/xpinstall/src/nsXPInstallManager.cpp
+++ /dev/null
@@ -1,1414 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 "nscore.h"
-#include "prprf.h"
-#include "plstr.h"
-
-#include "nsISupports.h"
-#include "nsIServiceManager.h"
-
-#include "nsIURL.h"
-#include "nsIFileURL.h"
-
-#include "nsITransport.h"
-#include "nsIOutputStream.h"
-#include "nsNetUtil.h"
-#include "nsIInputStream.h"
-#include "nsIFileStreams.h"
-#include "nsIStreamListener.h"
-#include "nsICryptoHash.h"
-
-#include "nsIExtensionManager.h"
-#include "nsSoftwareUpdateIIDs.h"
-
-#include "nsIStringEnumerator.h"
-#include "nsXPITriggerInfo.h"
-#include "nsXPInstallManager.h"
-#include "nsInstallTrigger.h"
-#include "nsIWindowWatcher.h"
-#include "nsIAuthPrompt.h"
-#include "nsIWindowMediator.h"
-#include "nsIDocument.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMWindow.h"
-#include "nsDirectoryService.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsAppDirectoryServiceDefs.h"
-
-#include "nsReadableUtils.h"
-#include "nsIPromptService.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsXPCOM.h"
-#include "nsISupportsPrimitives.h"
-#include "nsIObserverService.h"
-
-#include "nsISSLStatusProvider.h"
-#include "nsISSLStatus.h"
-#include "nsIX509Cert.h"
-#include "nsIX509Cert3.h"
-
-#include "nsIPrefService.h"
-#include "nsIPrefBranch.h"
-
-#include "CertReader.h"
-
-#include "nsEmbedCID.h"
-#include "nsIAsyncVerifyRedirectCallback.h"
-
-#define PREF_XPINSTALL_ENABLED                "xpinstall.enabled"
-#define PREF_XPINSTALL_CONFIRM_DLG            "xpinstall.dialog.confirm"
-#define PREF_XPINSTALL_STATUS_DLG_SKIN        "xpinstall.dialog.progress.skin"
-#define PREF_XPINSTALL_STATUS_DLG_CHROME      "xpinstall.dialog.progress.chrome"
-#define PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN   "xpinstall.dialog.progress.type.skin"
-#define PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME "xpinstall.dialog.progress.type.chrome"
-
-static NS_DEFINE_IID(kZipReaderCID,  NS_ZIPREADER_CID);
-
-
-nsXPInstallManager::nsXPInstallManager()
-  : mTriggers(0), mItem(0), mNextItem(0), mChromeType(NOT_CHROME),
-    mContentLength(0), mDialogOpen(false), mCancelled(false),
-    mNeedsShutdown(false), mFromChrome(false)
-{
-    // we need to own ourself because we have a longer
-    // lifetime than the scriptlet that created us.
-    NS_ADDREF_THIS();
-}
-
-
-nsXPInstallManager::~nsXPInstallManager()
-{
-    NS_ASSERT_OWNINGTHREAD(nsXPInstallManager);
-    NS_ASSERTION(!mTriggers, "Shutdown not called, triggers still alive");
-}
-
-
-NS_INTERFACE_MAP_BEGIN(nsXPInstallManager)
-  NS_INTERFACE_MAP_ENTRY(nsIXPIDialogService)
-  NS_INTERFACE_MAP_ENTRY(nsIXPInstallManager)
-  NS_INTERFACE_MAP_ENTRY(nsIObserver)
-  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
-  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
-  NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
-  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
-  NS_INTERFACE_MAP_ENTRY(nsPICertNotification)
-  NS_INTERFACE_MAP_ENTRY(nsIBadCertListener2)
-  NS_INTERFACE_MAP_ENTRY(nsISSLErrorListener)
-  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISupportsWeakReference)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_THREADSAFE_ADDREF(nsXPInstallManager)
-NS_IMPL_THREADSAFE_RELEASE(nsXPInstallManager)
-
-NS_IMETHODIMP
-nsXPInstallManager::InitManagerFromChrome(const PRUnichar **aURLs,
-                                          PRUint32 aURLCount,
-                                          nsIXPIProgressDialog* aListener)
-{
-    return InitManagerWithHashes(aURLs, nsnull, aURLCount, aListener);
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::InitManagerWithHashes(const PRUnichar **aURLs,
-                                          const char **aHashes,
-                                          PRUint32 aURLCount,
-                                          nsIXPIProgressDialog* aListener)
-{
-    // If Software Installation is not enabled, we don't want to proceed with
-    // update.
-    bool xpinstallEnabled = true;
-    nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
-    if (pref)
-        pref->GetBoolPref(PREF_XPINSTALL_ENABLED, &xpinstallEnabled);
-
-    if (!xpinstallEnabled)
-        return NS_OK;
-
-    mTriggers = new nsXPITriggerInfo();
-    if (!mTriggers)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    mNeedsShutdown = true;
-
-    for (PRUint32 i = 0; i < aURLCount; ++i)
-    {
-        nsXPITriggerItem* item = new nsXPITriggerItem(0, aURLs[i], nsnull,
-                                                      aHashes ? aHashes[i] : nsnull);
-        if (!item)
-        {
-            delete mTriggers; // nsXPITriggerInfo frees any alloc'ed nsXPITriggerItems
-            mTriggers = nsnull;
-            Shutdown();
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
-        mTriggers->Add(item);
-    }
-
-    mFromChrome = true;
-
-    nsresult rv = Observe(aListener, XPI_PROGRESS_TOPIC, NS_LITERAL_STRING("open").get());
-    if (NS_FAILED(rv))
-        Shutdown();
-    return rv;
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::InitManagerWithInstallInfo(nsIXPIInstallInfo* aInstallInfo)
-{
-    nsXPITriggerInfo* triggers;
-    nsresult rv = aInstallInfo->GetTriggerInfo(&triggers);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIDOMWindow> win;
-    rv = aInstallInfo->GetOriginatingWindow(getter_AddRefs(win));
-    if (NS_SUCCEEDED(rv))
-    {
-        PRUint32 type;
-        rv = aInstallInfo->GetChromeType(&type);
-        if (NS_SUCCEEDED(rv))
-        {
-            // Passing ownership onto InitManager which will free when necessary
-            aInstallInfo->SetTriggerInfo(nsnull);
-            return InitManager(win, triggers, type);
-        }
-    }
-
-    NS_RELEASE_THIS();
-    return rv;
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::InitManager(nsIDOMWindow* aParentWindow, nsXPITriggerInfo* aTriggers, PRUint32 aChromeType)
-{
-    if ( !aTriggers || aTriggers->Size() == 0 )
-    {
-        NS_WARNING("XPInstallManager called with no trigger info!");
-        delete aTriggers;
-        NS_RELEASE_THIS();
-        return NS_ERROR_INVALID_POINTER;
-    }
-
-    nsresult rv = NS_OK;
-
-    mNeedsShutdown = true;
-    mTriggers = aTriggers;
-    mChromeType = aChromeType;
-
-    mParentWindow = aParentWindow;
-
-    // Attempt to find a load group, continue if we can't find one though
-    if (aParentWindow) {
-        nsCOMPtr<nsIDOMDocument> domdoc;
-        rv = aParentWindow->GetDocument(getter_AddRefs(domdoc));
-        if (NS_SUCCEEDED(rv) && domdoc) {
-            nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
-            if (doc)
-                mLoadGroup = doc->GetDocumentLoadGroup();
-        }
-    }
-
-    // Start downloading initial chunks looking for signatures,
-    mOutstandingCertLoads = mTriggers->Size();
-
-    nsXPITriggerItem *item = mTriggers->Get(--mOutstandingCertLoads);
-
-    nsCOMPtr<nsIURI> uri;
-    NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(item->mURL));
-    nsCOMPtr<nsIStreamListener> listener = new CertReader(uri, nsnull, this);
-    if (listener)
-        rv = NS_OpenURI(listener, nsnull, uri, nsnull, mLoadGroup);
-    else
-        rv = NS_ERROR_OUT_OF_MEMORY;
-
-    if (NS_FAILED(rv)) {
-        Shutdown();
-    }
-    return rv;
-}
-
-
-nsresult
-nsXPInstallManager::InitManagerInternal()
-{
-    nsresult rv;
-    bool OKtoInstall = false; // initialize to secure state
-
-    //-----------------------------------------------------
-    // *** Do not return early after this point ***
-    //
-    // We have to clean up the triggers in case of error
-    //-----------------------------------------------------
-
-    // --- use embedding dialogs if any registered
-    nsCOMPtr<nsIXPIDialogService> dlgSvc(do_CreateInstance(NS_XPIDIALOGSERVICE_CONTRACTID));
-    if ( !dlgSvc )
-        dlgSvc = this; // provide our own dialogs
-
-    // --- prepare dialog params
-    PRUint32 numTriggers = mTriggers->Size();
-    PRUint32 numStrings = 4 * numTriggers;
-    const PRUnichar** packageList =
-        (const PRUnichar**)malloc( sizeof(PRUnichar*) * numStrings );
-
-    if ( packageList )
-    {
-        // populate the list. The list doesn't own the strings
-        for ( PRUint32 i=0, j=0; i < numTriggers; i++ )
-        {
-            nsXPITriggerItem *item = mTriggers->Get(i);
-            packageList[j++] = item->mName.get();
-            packageList[j++] = item->GetSafeURLString();
-            packageList[j++] = item->mIconURL.get();
-            packageList[j++] = item->mCertName.get();
-        }
-
-        //-----------------------------------------------------
-        // Get permission to install
-        //-----------------------------------------------------
-
-#ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
-        if ( mChromeType == CHROME_SKIN )
-        {
-            // We may want to enable the simple installation UI once
-            // bug 343037 is fixed
-
-            // skins get a simpler/friendlier dialog
-            // XXX currently not embeddable
-            OKtoInstall = ConfirmChromeInstall( mParentWindow, packageList );
-        }
-        else
-        {
-#endif
-            rv = dlgSvc->ConfirmInstall( mParentWindow,
-                                         packageList,
-                                         numStrings,
-                                         &OKtoInstall );
-            if (NS_FAILED(rv))
-                OKtoInstall = false;
-#ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
-        }
-#endif
-
-        if (OKtoInstall)
-        {
-            //-----------------------------------------------------
-            // Open the progress dialog
-            //-----------------------------------------------------
-
-            rv = dlgSvc->OpenProgressDialog( packageList, numStrings, this );
-        }
-    }
-    else
-        rv = NS_ERROR_OUT_OF_MEMORY;
-
-    //-----------------------------------------------------
-    // cleanup and signal callbacks if there were errors
-    //-----------------------------------------------------
-
-    if (packageList)
-        free(packageList);
-
-    PRInt32 cbstatus = 0;  // callback status
-    if (NS_FAILED(rv))
-        cbstatus = nsInstall::UNEXPECTED_ERROR;
-    else if (!OKtoInstall)
-        cbstatus = nsInstall::USER_CANCELLED;
-
-    if ( cbstatus != 0 )
-    {
-        // --- must shutdown if not continuing
-        Shutdown( cbstatus );
-    }
-
-    return rv;
-}
-
-
-NS_IMETHODIMP
-nsXPInstallManager::ConfirmInstall(nsIDOMWindow *aParent, const PRUnichar **aPackageList, PRUint32 aCount, bool *aRetval)
-{
-    *aRetval = false;
-
-    nsCOMPtr<nsIDOMWindow> parentWindow = aParent;
-    nsCOMPtr<nsIDialogParamBlock> params;
-    nsresult rv = LoadParams( aCount, aPackageList, getter_AddRefs(params) );
-
-    if ( NS_SUCCEEDED(rv) && parentWindow && params)
-    {
-        nsCOMPtr<nsIDOMWindow> newWindow;
-
-        nsCOMPtr<nsISupportsInterfacePointer> ifptr =
-            do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        ifptr->SetData(params);
-        ifptr->SetDataIID(&NS_GET_IID(nsIDialogParamBlock));
-
-        char* confirmDialogURL;
-        nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-        if (!pref)
-          return rv;
-
-        rv = pref->GetCharPref(PREF_XPINSTALL_CONFIRM_DLG, &confirmDialogURL);
-        NS_ASSERTION(NS_SUCCEEDED(rv), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.confirm");
-        if (NS_FAILED(rv))
-          return rv;
-
-        rv = parentWindow->OpenDialog(NS_ConvertASCIItoUTF16(confirmDialogURL),
-                                      NS_LITERAL_STRING("_blank"),
-                                      NS_LITERAL_STRING("chrome,centerscreen,modal,titlebar"),
-                                      ifptr,
-                                      getter_AddRefs(newWindow));
-
-        if (NS_SUCCEEDED(rv))
-        {
-            //Now get which button was pressed from the ParamBlock
-            PRInt32 buttonPressed = 0;
-            params->GetInt( 0, &buttonPressed );
-            *aRetval = buttonPressed ? false : true;
-        }
-    }
-
-    return rv;
-}
-
-#ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
-bool nsXPInstallManager::ConfirmChromeInstall(nsIDOMWindow* aParentWindow, const PRUnichar **aPackage)
-{
-    // get the dialog strings
-    nsXPIDLString applyNowText;
-    nsXPIDLString confirmText;
-    nsCOMPtr<nsIStringBundleService> bundleSvc =
-             do_GetService(NS_STRINGBUNDLE_CONTRACTID);
-    if (!bundleSvc)
-        return false;
-
-    nsCOMPtr<nsIStringBundle> xpiBundle;
-    bundleSvc->CreateBundle( XPINSTALL_BUNDLE_URL,
-                             getter_AddRefs(xpiBundle) );
-    if (!xpiBundle)
-        return false;
-
-    const PRUnichar *formatStrings[2] = { aPackage[0], aPackage[1] };
-    if ( mChromeType == CHROME_LOCALE )
-    {
-        xpiBundle->GetStringFromName(
-            NS_LITERAL_STRING("ApplyNowLocale").get(),
-            getter_Copies(applyNowText));
-        xpiBundle->FormatStringFromName(
-            NS_LITERAL_STRING("ConfirmLocale").get(),
-            formatStrings,
-            2,
-            getter_Copies(confirmText));
-    }
-    else
-    {
-        xpiBundle->GetStringFromName(
-            NS_LITERAL_STRING("ApplyNowSkin").get(),
-            getter_Copies(applyNowText));
-        xpiBundle->FormatStringFromName(
-            NS_LITERAL_STRING("ConfirmSkin").get(),
-            formatStrings,
-            2,
-            getter_Copies(confirmText));
-    }
-
-    if (confirmText.IsEmpty())
-        return false;
-
-    // confirmation dialog
-    bool bInstall = false;
-    nsCOMPtr<nsIPromptService> dlgService(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
-    if (dlgService)
-    {
-        dlgService->Confirm(
-            aParentWindow,
-            nsnull,
-            confirmText,
-            &bInstall );
-    }
-
-    return bInstall;
-}
-#endif
-
-NS_IMETHODIMP
-nsXPInstallManager::OpenProgressDialog(const PRUnichar **aPackageList, PRUint32 aCount, nsIObserver *aObserver)
-{
-    // --- convert parameters into nsISupportArray members
-    nsCOMPtr<nsIDialogParamBlock> list;
-    nsresult rv = LoadParams( aCount, aPackageList, getter_AddRefs(list) );
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsCOMPtr<nsISupportsInterfacePointer> listwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID));
-    if (listwrap) {
-        listwrap->SetData(list);
-        listwrap->SetDataIID(&NS_GET_IID(nsIDialogParamBlock));
-    }
-
-    nsCOMPtr<nsISupportsInterfacePointer> callbackwrap(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID));
-    if (callbackwrap) {
-        callbackwrap->SetData(aObserver);
-        callbackwrap->SetDataIID(&NS_GET_IID(nsIObserver));
-    }
-
-    nsCOMPtr<nsISupportsArray> params(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID));
-
-    if ( !params || !listwrap || !callbackwrap )
-        return NS_ERROR_FAILURE;
-
-    params->AppendElement(listwrap);
-    params->AppendElement(callbackwrap);
-
-    // --- open the window
-    nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
-    if (!wwatch)
-        return rv;
-
-    char *statusDialogURL, *statusDialogType;
-    nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-    if (!pref)
-        return rv;
-	const char* statusDlg = mChromeType == CHROME_SKIN ? PREF_XPINSTALL_STATUS_DLG_SKIN
-                                                         : PREF_XPINSTALL_STATUS_DLG_CHROME;
-	rv = pref->GetCharPref(statusDlg, &statusDialogURL);
-	NS_ASSERTION(NS_SUCCEEDED(rv), "Can't invoke XPInstall FE without a FE URL! Set xpinstall.dialog.status");
-	if (NS_FAILED(rv))
-		return rv;
-
-    const char* statusType = mChromeType == CHROME_SKIN ? PREF_XPINSTALL_STATUS_DLG_TYPE_SKIN
-                                                        : PREF_XPINSTALL_STATUS_DLG_TYPE_CHROME;
-    rv = pref->GetCharPref(statusType, &statusDialogType);
-    nsAutoString type;
-    type.AssignWithConversion(statusDialogType);
-    if (NS_SUCCEEDED(rv) && !type.IsEmpty()) {
-        nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
-
-        nsCOMPtr<nsIDOMWindow> recentWindow;
-        wm->GetMostRecentWindow(type.get(), getter_AddRefs(recentWindow));
-        if (recentWindow) {
-            nsCOMPtr<nsIObserverService> os =
-              mozilla::services::GetObserverService();
-            os->NotifyObservers(params, "xpinstall-download-started", nsnull);
-
-            recentWindow->Focus();
-            return NS_OK;
-        }
-    }
-
-    nsCOMPtr<nsIDOMWindow> newWindow;
-    rv = wwatch->OpenWindow(0,
-                            statusDialogURL,
-                            "_blank",
-                            "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable",
-                            params,
-                            getter_AddRefs(newWindow));
-
-    return rv;
-}
-
-
-NS_IMETHODIMP nsXPInstallManager::Observe( nsISupports *aSubject,
-                                           const char *aTopic,
-                                           const PRUnichar *aData )
-{
-    nsresult rv = NS_ERROR_ILLEGAL_VALUE;
-
-    if ( !aTopic || !aData )
-        return rv;
-
-    nsDependentCString topic( aTopic );
-    if ( topic.Equals( XPI_PROGRESS_TOPIC ) )
-    {
-        //------------------------------------------------------
-        // Communication from the XPInstall Progress Dialog
-        //------------------------------------------------------
-
-        nsDependentString data( aData );
-
-        if ( data.Equals( NS_LITERAL_STRING("open") ) )
-        {
-            // -- The dialog has been opened
-            if (mDialogOpen)
-                return NS_OK; // We've already been opened, nothing more to do
-
-            mDialogOpen = true;
-            rv = NS_OK;
-
-            nsCOMPtr<nsIObserverService> os =
-              mozilla::services::GetObserverService();
-            if (os)
-            {
-                os->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
-                os->AddObserver(this, "quit-application", true);
-            }
-
-            mDlg = do_QueryInterface(aSubject);
-
-            // -- get the ball rolling
-            DownloadNext();
-        }
-
-        else if ( data.Equals( NS_LITERAL_STRING("cancel") ) )
-        {
-            // -- The dialog/user wants us to cancel the download
-            mCancelled = true;
-            if ( !mDialogOpen )
-            {
-                // if we've never been opened then we can shutdown right here,
-                // otherwise we need to let mCancelled get discovered elsewhere
-                Shutdown();
-            }
-            rv = NS_OK;
-        }
-    }
-    else if ( topic.Equals( NS_IOSERVICE_GOING_OFFLINE_TOPIC ) ||
-              topic.Equals( "quit-application" ) )
-    {
-        mCancelled = true;
-        rv = NS_OK;
-    }
-
-    return rv;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-// Function name    : VerifySigning
-// Description      : Verify that the entire zip file is signed by the certificate displayed to
-//                    the user during download
-// Return type      : PRInt32
-// Argument         : nsIZipReader* hZip       - the zip reader
-// Argument         : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user
-//                    during download. Would have been retrieved from the first file in the zip
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-static nsresult
-VerifySigning(nsIZipReader* hZip, nsIPrincipal* aPrincipal)
-{
-    // If we didn't detect a principal from the zip file during download then
-    // we didn't suggest it was signed to the user, so just carry on.
-    if (!aPrincipal)
-        return NS_OK;
-
-    bool hasCert;
-    aPrincipal->GetHasCertificate(&hasCert);
-    if (!hasCert)
-        return NS_ERROR_FAILURE;
-
-    // See if the archive is signed at all first
-    nsCOMPtr<nsIPrincipal> principal;
-    nsresult rv = hZip->GetCertificatePrincipal(EmptyCString(), getter_AddRefs(principal));
-    if (NS_FAILED(rv) || !principal)
-        return NS_ERROR_FAILURE;
-
-    PRUint32 entryCount = 0;
-
-    // first verify all files in the jar are also in the manifest.
-    nsCOMPtr<nsIUTF8StringEnumerator> entries;
-    rv = hZip->FindEntries(EmptyCString(), getter_AddRefs(entries));
-    if (NS_FAILED(rv))
-        return rv;
-
-    bool more;
-    nsCAutoString name;
-    while (NS_SUCCEEDED(entries->HasMore(&more)) && more)
-    {
-        rv = entries->GetNext(name);
-        if (NS_FAILED(rv)) return rv;
-
-        // Do not verify the directory entries or
-        // entries which are in the meta-inf directory
-        if ((name.Last() == '/') ||
-            (PL_strncasecmp("META-INF/", name.get(), 9) == 0))
-            continue;
-
-        // Count the entries to be verified
-        entryCount++;
-
-        // Each entry must be signed
-        rv = hZip->GetCertificatePrincipal(name, getter_AddRefs(principal));
-        if (NS_FAILED(rv) || !principal) return NS_ERROR_FAILURE;
-
-        bool equal;
-        rv = principal->Equals(aPrincipal, &equal);
-        if (NS_FAILED(rv) || !equal) return NS_ERROR_FAILURE;
-    }
-
-    // next verify all files in the manifest are in the archive.
-    PRUint32 manifestEntryCount;
-    rv = hZip->GetManifestEntriesCount(&manifestEntryCount);
-    if (NS_FAILED(rv))
-        return rv;
-
-    if (entryCount != manifestEntryCount)
-        return NS_ERROR_FAILURE;  // some files were deleted from archive
-
-    return NS_OK;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-// Function name    : OpenAndValidateArchive
-// Description      : Opens install archive and validates contents
-// Return type      : PRInt32
-// Argument         : nsIZipReader* hZip       - the zip reader
-// Argument         : nsIFile* jarFile         - the .xpi file
-// Argument         : nsIPrincipal* aPrincipal - a principal, if any, displayed to the user
-//                    regarding the cert used to sign this install
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-static PRInt32
-OpenAndValidateArchive(nsIZipReader* hZip, nsIFile* jarFile, nsIPrincipal* aPrincipal)
-{
-    if (!jarFile)
-        return nsInstall::DOWNLOAD_ERROR;
-
-    nsCOMPtr<nsIFile> jFile;
-    nsresult rv =jarFile->Clone(getter_AddRefs(jFile));
-    if (NS_SUCCEEDED(rv))
-        rv = hZip->Open(jFile);
-
-    if (NS_FAILED(rv))
-        return nsInstall::CANT_READ_ARCHIVE;
-
-    // CRC check the integrity of all items in this archive
-    rv = hZip->Test(EmptyCString());
-    if (NS_FAILED(rv))
-    {
-        NS_WARNING("CRC check of archive failed!");
-        return nsInstall::CANT_READ_ARCHIVE;
-    }
-
-    rv = VerifySigning(hZip, aPrincipal);
-    if (NS_FAILED(rv))
-    {
-        NS_WARNING("Signing check of archive failed!");
-        return nsInstall::INVALID_SIGNATURE;
-    }
-
-    if (NS_FAILED(hZip->Test(nsDependentCString("install.rdf"))))
-    {
-        NS_WARNING("Archive did not contain an install manifest!");
-        return nsInstall::NO_INSTALL_SCRIPT;
-    }
-
-    return nsInstall::SUCCESS;
-}
-
-
-nsresult nsXPInstallManager::InstallItems()
-{
-    nsresult rv;
-    nsCOMPtr<nsIZipReader> hZip = do_CreateInstance(kZipReaderCID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIExtensionManager> em = do_GetService("@mozilla.org/extensions/manager;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // can't cancel from here on cause we can't undo installs in a multitrigger
-    for (PRUint32 i = 0; i < mTriggers->Size(); ++i)
-    {
-        mItem = (nsXPITriggerItem*)mTriggers->Get(i);
-        if ( !mItem || !mItem->mFile )
-        {
-            // notification for these errors already handled
-            continue;
-        }
-
-        // If there was hash info in the trigger, but
-        // there wasn't a hash object created, then the
-        // algorithm used isn't known.
-
-        if (mItem->mHashFound && !mItem->mHasher)
-        {
-            // report failure
-            mTriggers->SendStatus( mItem->mURL.get(), nsInstall::INVALID_HASH_TYPE );
-            if (mDlg)
-                mDlg->OnStateChange( i, nsIXPIProgressDialog::INSTALL_DONE,
-                                     nsInstall::INVALID_HASH_TYPE );
-            continue;
-        }
-
-        // Don't install if we can't verify the hash (if specified)
-        if (mItem->mHasher && !VerifyHash(mItem))
-        {
-            // report failure
-            mTriggers->SendStatus( mItem->mURL.get(), nsInstall::INVALID_HASH );
-            if (mDlg)
-                mDlg->OnStateChange( i, nsIXPIProgressDialog::INSTALL_DONE,
-                                     nsInstall::INVALID_HASH );
-            continue;
-        }
-
-        if (mDlg)
-            mDlg->OnStateChange( i, nsIXPIProgressDialog::INSTALL_START, 0 );
-
-        PRInt32 finalStatus = OpenAndValidateArchive( hZip,
-                                                      mItem->mFile,
-                                                      mItem->mPrincipal);
-        hZip->Close();
-
-        if (finalStatus == nsInstall::SUCCESS)
-        {
-            rv = em->InstallItemFromFile( mItem->mFile,
-                                          NS_INSTALL_LOCATION_APPPROFILE);
-            if (NS_FAILED(rv))
-                finalStatus = nsInstall::EXECUTION_ERROR;
-        }
-
-        mTriggers->SendStatus( mItem->mURL.get(), finalStatus );
-        if (mDlg)
-            mDlg->OnStateChange( i, nsIXPIProgressDialog::INSTALL_DONE,
-                                 finalStatus );
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP nsXPInstallManager::DownloadNext()
-{
-    nsresult rv = NS_OK;
-    mContentLength = 0;
-
-    if (mCancelled)
-    {
-        // Don't download any more if we were cancelled
-        Shutdown();
-        return NS_OK;
-    }
-
-    if ( mNextItem < mTriggers->Size() )
-    {
-        //-------------------------------------------------
-        // There are items to download, get the next one
-        //-------------------------------------------------
-        mItem = (nsXPITriggerItem*)mTriggers->Get(mNextItem++);
-
-        NS_ASSERTION( mItem, "bogus Trigger slipped through" );
-        NS_ASSERTION( !mItem->mURL.IsEmpty(), "bogus trigger");
-        if ( !mItem || mItem->mURL.IsEmpty() )
-        {
-            // serious problem with trigger! Can't notify anyone of the
-            // error without the URL, just try to carry on.
-            return DownloadNext();
-        }
-
-        // --- Tell the dialog we're starting a download
-        if (mDlg)
-            mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::DOWNLOAD_START, 0 );
-
-        if ( mItem->IsFileURL() && mChromeType == NOT_CHROME )
-        {
-            //--------------------------------------------------
-            // Already local, we can open it where it is
-            //--------------------------------------------------
-            nsCOMPtr<nsIURI> pURL;
-            rv = NS_NewURI(getter_AddRefs(pURL), mItem->mURL);
-
-            if (NS_SUCCEEDED(rv))
-            {
-                nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(pURL,&rv);
-                if (fileURL)
-                {
-                    nsCOMPtr<nsIFile> localFile;
-                    rv = fileURL->GetFile(getter_AddRefs(localFile));
-                    if (NS_SUCCEEDED(rv))
-                    {
-                        mItem->mFile = do_QueryInterface(localFile,&rv);
-                    }
-                }
-            }
-
-            if ( NS_FAILED(rv) || !mItem->mFile )
-            {
-                // send error status back
-                if (mDlg)
-                    mDlg->OnStateChange( mNextItem-1,
-                                         nsIXPIProgressDialog::INSTALL_DONE,
-                                         nsInstall::UNEXPECTED_ERROR );
-                mTriggers->SendStatus( mItem->mURL.get(),
-                                       nsInstall::UNEXPECTED_ERROR );
-                mItem->mFile = 0;
-            }
-            else if (mDlg)
-            {
-                mDlg->OnStateChange( mNextItem-1,
-                                     nsIXPIProgressDialog::DOWNLOAD_DONE, 0);
-            }
-
-            // --- on to the next one
-            return DownloadNext();
-        }
-        else
-        {
-            //--------------------------------------------------
-            // We have one to download
-            //--------------------------------------------------
-            rv = GetDestinationFile(mItem->mURL, getter_AddRefs(mItem->mFile));
-            if (NS_SUCCEEDED(rv))
-            {
-                nsCOMPtr<nsIURI> pURL;
-                rv = NS_NewURI(getter_AddRefs(pURL), mItem->mURL);
-                if (NS_SUCCEEDED(rv))
-                {
-                    nsCOMPtr<nsIChannel> channel;
-
-                    rv = NS_NewChannel(getter_AddRefs(channel), pURL, nsnull, mLoadGroup, this);
-                    if (NS_SUCCEEDED(rv))
-                    {
-                        rv = channel->AsyncOpen(this, nsnull);
-                    }
-                }
-            }
-
-            if (NS_FAILED(rv))
-            {
-                // announce failure
-                if (mDlg)
-                    mDlg->OnStateChange( mNextItem-1,
-                                         nsIXPIProgressDialog::INSTALL_DONE,
-                                         nsInstall::DOWNLOAD_ERROR );
-                mTriggers->SendStatus( mItem->mURL.get(),
-                                       nsInstall::DOWNLOAD_ERROR );
-                mItem->mFile = 0;
-
-                // We won't get Necko callbacks so start the next one now
-                return DownloadNext();
-            }
-        }
-    }
-    else
-    {
-        //------------------------------------------------------
-        // all downloaded, install them
-        //------------------------------------------------------
-        InstallItems();
-        Shutdown();
-    }
-
-    return rv;
-}
-
-
-//-------------------------------------------------------------------
-// VerifyHash
-//
-// Returns true if the file hash matches the expected value (or if
-// the item has no hash value). False if we can't verify the hash
-// for any reason
-//
-bool nsXPInstallManager::VerifyHash(nsXPITriggerItem* aItem)
-{
-    NS_ASSERTION(aItem, "Null nsXPITriggerItem passed to VerifyHash");
-
-    nsresult rv;
-    if (!aItem->mHasher)
-      return false;
-
-    nsCOMPtr<nsIInputStream> stream;
-    rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aItem->mFile);
-    if (NS_FAILED(rv)) return false;
-
-    rv = aItem->mHasher->UpdateFromStream(stream, PR_UINT32_MAX);
-    if (NS_FAILED(rv)) return false;
-
-    nsCAutoString binaryHash;
-    rv = aItem->mHasher->Finish(false, binaryHash);
-    if (NS_FAILED(rv)) return false;
-
-    char* hash = nsnull;
-    for (PRUint32 i=0; i < binaryHash.Length(); ++i)
-    {
-        hash = PR_sprintf_append(hash,"%.2x", (PRUint8)binaryHash[i]);
-    }
-
-    bool result = aItem->mHash.EqualsIgnoreCase(hash);
-
-    PR_smprintf_free(hash);
-    return result;
-}
-
-
-void nsXPInstallManager::Shutdown(PRInt32 status)
-{
-    if (mDlg)
-    {
-        // tell the dialog it can go away
-        mDlg->OnStateChange(0, nsIXPIProgressDialog::DIALOG_CLOSE, 0 );
-        mDlg = nsnull;
-    }
-
-    if (mNeedsShutdown)
-    {
-        mNeedsShutdown = false;
-
-        // Send remaining status notifications if we were cancelled early
-        nsXPITriggerItem* item;
-        while ( mNextItem < mTriggers->Size() )
-        {
-            item = (nsXPITriggerItem*)mTriggers->Get(mNextItem++);
-            if ( item && !item->mURL.IsEmpty() )
-            {
-                mTriggers->SendStatus( item->mURL.get(), status );
-            }
-        }
-
-        // Clean up downloaded files (regular install only, not chrome installs)
-        for (PRUint32 i = 0; i < mTriggers->Size(); i++ )
-        {
-            item = static_cast<nsXPITriggerItem*>(mTriggers->Get(i));
-            if ( item && item->mFile && !item->IsFileURL() )
-                item->mFile->Remove(false);
-        }
-
-        nsCOMPtr<nsIObserverService> os =
-          mozilla::services::GetObserverService();
-        if (os)
-        {
-            os->RemoveObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC);
-            os->RemoveObserver(this, "quit-application");
-        }
-
-        if (mTriggers)
-        {
-            delete mTriggers;
-            mTriggers = nsnull;
-        }
-
-        NS_RELEASE_THIS();
-    }
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::LoadParams(PRUint32 aCount, const PRUnichar** aPackageList, nsIDialogParamBlock** aParams)
-{
-    nsresult rv;
-    nsCOMPtr<nsIDialogParamBlock> paramBlock = do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv))
-    {
-        // set OK and Cancel buttons
-        paramBlock->SetInt( 0, 2 );
-        // pass in number of strings
-        paramBlock->SetInt( 1, aCount );
-        // add strings
-        paramBlock->SetNumberStrings( aCount );
-        for (PRUint32 i = 0; i < aCount; i++)
-            paramBlock->SetString( i, aPackageList[i] );
-    }
-
-    NS_IF_ADDREF(*aParams = paramBlock);
-    return rv;
-}
-
-
-NS_IMETHODIMP
-nsXPInstallManager::GetDestinationFile(nsString& url, nsILocalFile* *file)
-{
-    NS_ENSURE_ARG_POINTER(file);
-    nsresult rv;
-
-    nsCOMPtr<nsIProperties> directoryService =
-             do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsILocalFile> temp;
-    rv = directoryService->Get(NS_OS_TEMP_DIR,
-                               NS_GET_IID(nsIFile),
-                               getter_AddRefs(temp));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    temp->AppendNative(NS_LITERAL_CSTRING("tmp.xpi"));
-    temp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
-    *file = temp;
-    NS_IF_ADDREF(*file);
-
-    return NS_OK;
-}
-
-nsresult
-nsXPInstallManager::CheckCert(nsIChannel* aChannel)
-{
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCAutoString scheme;
-    rv = uri->GetScheme(scheme);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!scheme.Equals(NS_LITERAL_CSTRING("https")))
-        return NS_OK;
-
-    nsCOMPtr<nsISupports> security;
-    rv = aChannel->GetSecurityInfo(getter_AddRefs(security));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsISSLStatusProvider> statusProvider(do_QueryInterface(security));
-    NS_ENSURE_TRUE(statusProvider, NS_ERROR_FAILURE);
-
-    rv = statusProvider->GetSSLStatus(getter_AddRefs(security));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsISSLStatus> status(do_QueryInterface(security));
-    NS_ENSURE_TRUE(status, NS_ERROR_FAILURE);
-    nsCOMPtr<nsIX509Cert> cert;
-    rv = status->GetServerCert(getter_AddRefs(cert));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIX509Cert> issuer;
-    rv = cert->GetIssuer(getter_AddRefs(issuer));
-    NS_ENSURE_SUCCESS(rv, rv);
-    bool equal;
-    while (issuer && NS_SUCCEEDED(cert->Equals(issuer, &equal)) && !equal) {
-        cert = issuer;
-        rv = cert->GetIssuer(getter_AddRefs(issuer));
-        NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    if (issuer) {
-        PRUint32 length;
-        PRUnichar** tokenNames;
-        nsCOMPtr<nsIX509Cert3> issuer2(do_QueryInterface(issuer));
-        NS_ENSURE_TRUE(status, NS_ERROR_FAILURE);
-        rv = issuer2->GetAllTokenNames(&length, &tokenNames);
-        NS_ENSURE_SUCCESS(rv ,rv);
-        for (PRUint32 i = 0; i < length; i++) {
-            if (nsDependentString(tokenNames[i]).Equals(NS_LITERAL_STRING("Builtin Object Token")))
-                return NS_OK;
-        }
-    }
-    return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
-{
-    nsresult rv = NS_ERROR_FAILURE;
-
-    // If we are dealing with a HTTP request, then treat HTTP error pages as
-    // download failures.
-    nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(request);
-    if (httpChan) {
-        // If we were chrome lauched check the certificate on the request
-        if (mFromChrome && NS_FAILED(CheckCert(httpChan))) {
-            request->Cancel(NS_BINDING_ABORTED);
-            return NS_OK;
-        }
-        bool succeeded;
-        if (NS_SUCCEEDED(httpChan->GetRequestSucceeded(&succeeded)) && !succeeded) {
-            // HTTP response is not a 2xx!
-            request->Cancel(NS_BINDING_ABORTED);
-            return NS_OK;
-        }
-    }
-
-    if (mLoadGroup)
-        mLoadGroup->RemoveRequest(request, nsnull, NS_BINDING_RETARGETED);
-
-    NS_ASSERTION( mItem && mItem->mFile, "XPIMgr::OnStartRequest bad state");
-    if ( mItem && mItem->mFile )
-    {
-        NS_ASSERTION( !mItem->mOutStream, "Received double OnStartRequest from Necko");
-
-        rv = NS_NewLocalFileOutputStream(getter_AddRefs(mItem->mOutStream),
-                                         mItem->mFile,
-                                         PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
-                                         0600);
-    }
-    return rv;
-}
-
-
-NS_IMETHODIMP
-nsXPInstallManager::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
-                                  nsresult status)
-{
-    nsresult rv;
-
-    switch( status )
-    {
-
-        case NS_BINDING_SUCCEEDED:
-            NS_ASSERTION( mItem->mOutStream, "XPIManager: output stream doesn't exist");
-            rv = NS_OK;
-            break;
-
-        case NS_BINDING_FAILED:
-        case NS_BINDING_ABORTED:
-            rv = status;
-            // XXX need to note failure, both to send back status
-            // to the callback, and also so we don't try to install
-            // this probably corrupt file.
-            break;
-
-        default:
-            rv = NS_ERROR_ILLEGAL_VALUE;
-    }
-
-    NS_ASSERTION( mItem, "Bad state in XPIManager");
-    if ( mItem && mItem->mOutStream )
-    {
-        mItem->mOutStream->Close();
-        mItem->mOutStream = nsnull;
-    }
-
-    if (NS_FAILED(rv) || mCancelled)
-    {
-        // Download error!
-        // -- first clean up partially downloaded file
-        if ( mItem && mItem->mFile )
-        {
-            bool flagExists;
-            nsresult rv2 ;
-            rv2 = mItem->mFile->Exists(&flagExists);
-            if (NS_SUCCEEDED(rv2) && flagExists)
-                mItem->mFile->Remove(false);
-
-            mItem->mFile = 0;
-        }
-
-        // -- then notify interested parties
-        PRInt32 errorcode = mCancelled ? nsInstall::USER_CANCELLED
-                                       : nsInstall::DOWNLOAD_ERROR;
-        if (mDlg)
-            mDlg->OnStateChange( mNextItem-1,
-                                 nsIXPIProgressDialog::INSTALL_DONE,
-                                 errorcode );
-        if (mItem)
-            mTriggers->SendStatus( mItem->mURL.get(), errorcode );
-    }
-    else if (mDlg)
-    {
-        mDlg->OnStateChange( mNextItem-1, nsIXPIProgressDialog::DOWNLOAD_DONE, 0);
-    }
-
-    DownloadNext();
-    return rv;
-}
-
-
-NS_IMETHODIMP
-nsXPInstallManager::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
-                                    nsIInputStream *pIStream,
-                                    PRUint32 sourceOffset,
-                                    PRUint32 length)
-{
-#define XPI_ODA_BUFFER_SIZE 8*1024
-    PRUint32 amt = NS_MIN(XPI_ODA_BUFFER_SIZE, length);
-    nsresult err;
-    char buffer[XPI_ODA_BUFFER_SIZE];
-    PRUint32 writeCount;
-
-    if (mCancelled)
-    {
-        // We must cancel this download in progress. We may get extra
-        // OnData calls if they were already queued so beware
-        request->Cancel(NS_BINDING_ABORTED);
-        return NS_ERROR_FAILURE;
-    }
-
-    do
-    {
-        err = pIStream->Read(buffer, amt, &amt);
-
-        if (amt == 0) break;
-        if (NS_FAILED(err)) return err;
-
-        err = mItem->mOutStream->Write( buffer, amt, &writeCount);
-        if (NS_FAILED(err) || writeCount != amt)
-        {
-            return NS_ERROR_FAILURE;
-        }
-        length -= amt;
-
-        amt = NS_MIN(XPI_ODA_BUFFER_SIZE, length);
-
-    } while (length > 0);
-
-    return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsXPInstallManager::OnProgress(nsIRequest* request, nsISupports *ctxt, PRUint64 aProgress, PRUint64 aProgressMax)
-{
-    nsresult rv = NS_OK;
-
-    if (mDlg && !mCancelled)
-    {
-        if (mContentLength < 1) {
-            nsCOMPtr<nsIChannel> channel = do_QueryInterface(request,&rv);
-            NS_ASSERTION(channel, "should have a channel");
-            if (NS_FAILED(rv)) return rv;
-            rv = channel->GetContentLength(&mContentLength);
-            if (NS_FAILED(rv)) return rv;
-        }
-        // XXX once channels support that, use 64-bit contentlength
-        rv = mDlg->OnProgress( mNextItem-1, aProgress, PRUint64(mContentLength) );
-    }
-
-    return rv;
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::OnStatus(nsIRequest* request, nsISupports *ctxt,
-                             nsresult aStatus, const PRUnichar *aStatusArg)
-{
-    // don't need to do anything
-    return NS_OK;
-}
-
-// nsIInterfaceRequestor method
-NS_IMETHODIMP
-nsXPInstallManager::GetInterface(const nsIID & eventSinkIID, void* *_retval)
-{
-    if (eventSinkIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
-        *_retval = nsnull;
-
-        nsresult rv;
-        nsCOMPtr<nsIWindowWatcher> ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCOMPtr<nsIAuthPrompt> prompt;
-        rv = ww->GetNewAuthPrompter(nsnull, getter_AddRefs(prompt));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsIAuthPrompt *p = prompt.get();
-        NS_ADDREF(p);
-        *_retval = p;
-        return NS_OK;
-    }
-    else if (eventSinkIID.Equals(NS_GET_IID(nsIBadCertListener2))) {
-        // If we aren't chrome triggered fall back to the default dialogs
-        if (!mFromChrome)
-            return NS_ERROR_NO_INTERFACE;
-    }
-    return QueryInterface(eventSinkIID, (void**)_retval);
-}
-
-// nsIChannelEventSink method
-NS_IMETHODIMP
-nsXPInstallManager::AsyncOnChannelRedirect(nsIChannel *oldChannel,
-                                           nsIChannel *newChannel,
-                                           PRUint32 flags,
-                                           nsIAsyncVerifyRedirectCallback *callback)
-{
-    // Chrome triggered installs need to have their certificates checked
-    if (mFromChrome) {
-        nsresult rv = CheckCert(oldChannel);
-        if (NS_FAILED(rv()))
-            return rv;
-    }
-
-    callback->OnRedirectVerifyCallback(NS_OK);
-    return NS_OK;
-}
-
-// nsIBadCertListener2 methods
-NS_IMETHODIMP
-nsXPInstallManager::NotifyCertProblem(nsIInterfaceRequestor *socketInfo,
-                                      nsISSLStatus *status,
-                                      const nsACString &targetSite,
-                                      bool *_retval)
-{
-    *_retval = true;
-    return NS_OK;
-}
-
-// nsISSLErrorListener methods
-NS_IMETHODIMP
-nsXPInstallManager::NotifySSLError(nsIInterfaceRequestor *socketInfo, 
-                                    PRInt32 error, 
-                                    const nsACString &targetSite, 
-                                    bool *_retval)
-{
-    *_retval = true;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsXPInstallManager::OnCertAvailable(nsIURI *aURI,
-                                    nsISupports* context,
-                                    nsresult aStatus,
-                                    nsIPrincipal *aPrincipal)
-{
-    if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED) {
-        // Check for a bad status.  The only acceptable failure status code we accept
-        // is NS_BINDING_ABORTED.  For all others we want to ensure that the
-        // nsIPrincipal is nsnull.
-
-        NS_ASSERTION(aPrincipal == nsnull, "There has been an error, but we have a principal!");
-        aPrincipal = nsnull;
-    }
-
-    // get the current one and assign the cert name
-    nsXPITriggerItem *item = mTriggers->Get(mOutstandingCertLoads);
-    item->SetPrincipal(aPrincipal);
-
-    if (mOutstandingCertLoads == 0) {
-        InitManagerInternal();
-        return NS_OK;
-    }
-
-    // get the next one to load.  If there is any failure, we just go on to the
-    // next trigger.  When all triggers items are handled, we call into InitManagerInternal
-
-    item = mTriggers->Get(--mOutstandingCertLoads);
-
-    nsCOMPtr<nsIURI> uri;
-    NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(item->mURL.get()).get());
-
-    if (!uri || mChromeType != NOT_CHROME)
-        return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull);
-
-    nsIStreamListener* listener = new CertReader(uri, nsnull, this);
-    if (!listener)
-        return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull);
-
-    NS_ADDREF(listener);
-    nsresult rv = NS_OpenURI(listener, nsnull, uri, nsnull, mLoadGroup);
-
-    NS_ASSERTION(NS_SUCCEEDED(rv), "OpenURI failed");
-    NS_RELEASE(listener);
-
-    if (NS_FAILED(rv))
-        return OnCertAvailable(uri, context, NS_ERROR_FAILURE, nsnull);
-
-    return NS_OK;
-}
-
deleted file mode 100644
--- a/xpinstall/src/nsXPInstallManager.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/* ***** 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 Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Daniel Veditz <dveditz@netscape.com>
- *   Dave Townsend <dtownsend@oxymoronical.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 _NS_XPINSTALLMANAGER_H
-#define _NS_XPINSTALLMANAGER_H
-
-#include "nsInstall.h"
-
-#include "nscore.h"
-#include "nsISupports.h"
-#include "nsString.h"
-
-#include "nsIURL.h"
-#include "nsIInputStream.h"
-#include "nsIStreamListener.h"
-#include "nsIXPInstallManager.h"
-#include "nsIXPIDialogService.h"
-#include "nsXPITriggerInfo.h"
-#include "nsIXPIProgressDialog.h"
-#include "nsIChromeRegistry.h"
-#include "nsIDOMWindow.h"
-#include "nsIObserver.h"
-#include "nsIBadCertListener2.h"
-#include "nsISSLErrorListener.h"
-#include "nsIChannelEventSink.h"
-#include "nsIZipReader.h"
-#include "nsIXPIInstallInfo.h"
-#include "nsILoadGroup.h"
-
-#include "nsCOMPtr.h"
-
-#include "nsIProgressEventSink.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
-
-#include "nsIDialogParamBlock.h"
-
-#include "nsPICertNotification.h"
-
-#include "nsWeakReference.h"
-
-#define NS_XPIDIALOGSERVICE_CONTRACTID "@mozilla.org/embedui/xpinstall-dialog-service;1"
-#define NS_XPINSTALLMANAGERCOMPONENT_CONTRACTID "@mozilla.org/xpinstall/install-manager;1"
-#define XPI_PROGRESS_TOPIC "xpinstall-progress"
-
-class nsXPInstallManager : public nsIXPIDialogService,
-                           public nsIXPInstallManager,
-                           public nsIObserver,
-                           public nsIStreamListener,
-                           public nsIProgressEventSink,
-                           public nsIInterfaceRequestor,
-                           public nsPICertNotification,
-                           public nsIBadCertListener2,
-                           public nsISSLErrorListener,
-                           public nsIChannelEventSink,
-                           public nsSupportsWeakReference
-{
-    public:
-        nsXPInstallManager();
-        virtual ~nsXPInstallManager();
-
-        NS_DECL_ISUPPORTS
-        NS_DECL_NSIXPIDIALOGSERVICE
-        NS_DECL_NSIXPINSTALLMANAGER
-        NS_DECL_NSIOBSERVER
-        NS_DECL_NSISTREAMLISTENER
-        NS_DECL_NSIPROGRESSEVENTSINK
-        NS_DECL_NSIREQUESTOBSERVER
-        NS_DECL_NSIINTERFACEREQUESTOR
-        NS_DECL_NSPICERTNOTIFICATION
-        NS_DECL_NSIBADCERTLISTENER2
-        NS_DECL_NSISSLERRORLISTENER
-        NS_DECL_NSICHANNELEVENTSINK
-
-        NS_IMETHOD InitManager(nsIDOMWindow* aParentWindow, nsXPITriggerInfo* aTrigger, PRUint32 aChromeType );
-
-    private:
-        nsresult    InitManagerInternal();
-        nsresult    InstallItems();
-        NS_IMETHOD  DownloadNext();
-        void        Shutdown(PRInt32 status = nsInstall::USER_CANCELLED);
-        NS_IMETHOD  GetDestinationFile(nsString& url, nsILocalFile* *file);
-        NS_IMETHOD  LoadParams(PRUint32 aCount, const PRUnichar** aPackageList, nsIDialogParamBlock** aParams);
-#ifdef ENABLE_SKIN_SIMPLE_INSTALLATION_UI
-        bool        ConfirmChromeInstall(nsIDOMWindow* aParentWindow, const PRUnichar** aPackage);
-#endif
-        bool        VerifyHash(nsXPITriggerItem* aItem);
-        PRInt32     GetIndexFromURL(const PRUnichar* aUrl);
-        nsresult    CheckCert(nsIChannel* aChannel);
-
-        nsXPITriggerInfo*   mTriggers;
-        nsXPITriggerItem*   mItem;
-        PRUint32            mNextItem;
-        PRUint32            mChromeType;
-        PRInt32             mContentLength;
-        PRInt32             mOutstandingCertLoads;
-        bool                mDialogOpen;
-        bool                mCancelled;
-        bool                mNeedsShutdown;
-        bool                mFromChrome;
-
-        nsCOMPtr<nsIXPIProgressDialog>  mDlg;
-
-        nsCOMPtr<nsIDOMWindow>          mParentWindow;
-        nsCOMPtr<nsILoadGroup>          mLoadGroup;
-};
-
-#endif
deleted file mode 100644
index ca333944cb5797e387b9dca07777ac1f1d73cf62..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/xpinstall/test/pre_checkin_trigger.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<HTML>
-<!-- ***** 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 Communicator client code, released March
-   - 31, 1998.
-   -
-   - The Initial Developer of the Original Code is
-   - Netscape Communications Corporation.
-   - Portions created by the Initial Developer are Copyright (C) 1998-1999
-   - the Initial Developer. All Rights Reserved.
-   -
-   - Contributor(s):
-   -        Samir Gehani <sgehani@netscape.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 LGPL or the GPL. 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 ***** -->
-<HEAD>
-<TITLE>XPInstall Pre-Checkin Trigger Test</TITLE>
-
-<SCRIPT>
-
-function xpinstallCallback(url, status)
-{
-  if (status == 0)
-    msg = "XPInstall Test:   PASSED\n";
-  else
-    msg = "XPInstall Test:   FAILED\n";
-
-  dump(msg);
-  alert(msg);
-}
-
-</SCRIPT>
-</HEAD>
-
-<BODY>
-
-<H3>XPInstall Pre-Checkin Trigger Test</H3>
-<HR>
-Click on the link below to execute the XPInstall pre-checkin test. <BR>
-The test result (PASS or FAIL) will pop up in an alert after the test completes. <P>
-
-<A HREF="javascript:
-
-  xpi={'XPInstall Pre-Checkin Test':'pre_checkin.xpi'};
-  InstallTrigger.install(xpi,xpinstallCallback);
-
-">Test XPInstall</A>
-
-
-</BODY>
-</HTML>
deleted file mode 100644
--- a/xpinstall/test/testXPIDialogService.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/* ***** 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 XPInstall.
- *
- * 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 Veditz <dveditz@netscape.com>  (Original Author)
- *
- * 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 ***** */
-
-/* implementation of a test XPInstall Dialog Service */
-
-// -----------------------------------------------------------------------
-// Test the XPInstall embedding API's by dropping this component into
-// the Mozilla components directory and registering it.
-//
-// Do not export as part of a normal build since this will override the
-// built-in Mozilla UI we want to use.
-// -----------------------------------------------------------------------
-
-// -----------------------------------------------------------------------
-// constants
-// -----------------------------------------------------------------------
-const XPIDIALOGSERVICE_CONTRACTID =
-    "@mozilla.org/embedui/xpinstall-dialog-service;1";
-
-const XPIDIALOGSERVICE_CID =
-    Components.ID("{9A5BEF68-3FDA-4926-9809-87A5A1CC8505}");
-
-const XPI_TOPIC = "xpinstall-progress";
-const OPEN      = "open";
-const CANCEL    = "cancel";
-
-
-// -----------------------------------------------------------------------
-// XPInstall Dialog Service
-// -----------------------------------------------------------------------
-
-function testXPIDialogService() {}
-
-testXPIDialogService.prototype =
-{
-    QueryInterface: function( iid )
-    {
-        if (iid.equals(Components.interfaces.nsIXPIDialogService) ||
-            iid.equals(Components.interfaces.nsIXPIProgressDialog) ||
-            iid.equals(Components.interfaces.nsISupports))
-            return this;
-
-        throw Components.results.NS_ERROR_NO_INTERFACE;
-    },
-
-    confirmInstall: function( parent, packages, count )
-    {
-        // stash parent window for use later
-        this.mParent = parent;
-
-        // quick and dirty data display
-        var str = "num packages: " + count/2 + "\n\n";
-        for ( i = 0; i < count; ++i)
-            str += packages[i++] + ' -- ' + packages[i] + '\n';
-
-        str += "\nDo you want to install?";
-
-        return parent.confirm(str);
-    },
-
-    openProgressDialog: function( packages, count, mgr )
-    {
-        this.dlg = this.mParent.open();
-        mgr.observe( this, XPI_TOPIC, OPEN );
-    },
-
-    onStateChange: function( index, state, error )
-    {
-        dump("---XPIDlg--- State: "+index+', '+state+', '+error+'\n');
-    },
-
-    onProgress: function( index, value, max )
-    {
-        dump("---XPIDlg---     "+index+": "+value+' of '+max+'\n');
-    }
-};
-
-
-
-// -----------------------------------------------------------------------
-// XPInstall Dialog Service Module and Factory
-// -----------------------------------------------------------------------
-
-// --- module entry point ---
-function NSGetModule(compMgr, fileSpec) { return XPIDlgSvcModule; }
-
-
-// --- module ---
-var XPIDlgSvcModule =
-{
-    registerSelf: function( compMgr, fileSpec, location, type )
-    {
-        compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
-
-        compMgr.registerFactoryLocation(XPIDIALOGSERVICE_CID,
-            'XPInstall Dialog Service test component',
-            XPIDIALOGSERVICE_CONTRACTID, fileSpec,
-            location, type);
-    },
-
-    unregisterSelf: function( compMgr, fileSpec, location )
-    {
-        compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
-        compMgr.unregisterFactoryLocation(XPIDIALOGSERVICE_CID, fileSpec);
-    },
-
-    getClassObject: function( compMgr, cid, iid )
-    {
-        if (!cid.equals(XPIDIALOGSERVICE_CID))
-            throw Components.results.NS_ERROR_NO_INTERFACE;
-
-        if (!iid.equals(Components.interfaces.nsIFactory))
-            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-
-        return XPIDlgSvcFactory;
-    },
-
-    canUnload: function( compMgr ) { return true; }
-};
-
-
-// --- factory ---
-var XPIDlgSvcFactory =
-{
-    createInstance: function( outer, iid )
-    {
-        if (outer != null)
-            throw Components.results.NS_ERROR_NO_AGGREGATION;
-
-        if (!iid.equals(Components.interfaces.nsIXPIDialogService) &&
-            !iid.equals(Components.interfaces.nsISupports))
-            throw Components.results.NS_ERROR_INVALID_ARG;
-
-        return new testXPIDialogService();
-    }
-};
deleted file mode 100644
--- a/xpinstall/tests/Makefile.in
+++ /dev/null
@@ -1,108 +0,0 @@
-# ***** 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
-# Mozilla Foundation.
-# Portions created by the Initial Developer are Copyright (C) 2008
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-#
-# Alternatively, the contents of this file may be used under the terms of
-# either of 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 *****
-
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
-relativesrcdir  = xpinstall/tests
-
-include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
-
-ifneq (mobile,$(MOZ_BUILD_APP))
-_BROWSER_FILES = harness.js \
-                 browser_unsigned_url.js \
-                 browser_unsigned_trigger.js \
-                 browser_whitelist.js \
-                 browser_whitelist2.js \
-                 browser_whitelist3.js \
-                 browser_whitelist4.js \
-                 browser_whitelist5.js \
-                 browser_whitelist6.js \
-                 browser_hash.js \
-                 browser_badhash.js \
-                 browser_badhashtype.js \
-                 browser_signed_url.js \
-                 browser_signed_trigger.js \
-                 browser_signed_untrusted.js \
-                 browser_signed_tampered.js \
-                 browser_signed_multiple.js \
-                 browser_signed_naming.js \
-                 browser_empty.js \
-                 browser_corrupt.js \
-                 browser_cookies.js \
-                 browser_cookies2.js \
-                 browser_cookies3.js \
-                 browser_cookies4.js \
-                 browser_enabled.js \
-                 browser_enabled2.js \
-                 browser_enabled3.js \
-                 browser_softwareupdate.js \
-                 browser_installchrome.js \
-                 browser_opendialog.js \
-                 browser_localfile.js \
-                 browser_localfile2.js \
-                 browser_auth.js \
-                 browser_auth2.js \
-                 browser_auth3.js \
-                 browser_offline.js \
-                 browser_chrome.js \
-                 browser_cancel.js \
-                 browser_navigateaway.js \
-                 browser_navigateaway2.js \
-                 browser_bug540558.js \
-                 unsigned.xpi \
-                 signed.xpi \
-                 signed2.xpi \
-                 signed-no-o.xpi \
-                 signed-no-cn.xpi \
-                 signed-untrusted.xpi \
-                 signed-tampered.xpi \
-                 empty.xpi \
-                 corrupt.xpi \
-                 enabled.html \
-                 installtrigger.html \
-                 startsoftwareupdate.html \
-                 installchrome.html \
-                 authRedirect.sjs \
-                 cookieRedirect.sjs \
-                 bug540558.html \
-                 $(NULL)
-
-libs::	$(_BROWSER_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
-endif
deleted file mode 100644
--- a/xpinstall/tests/authRedirect.sjs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Simple script redirects to the query part of the uri if the browser
-// authenticates with username "testuser" password "testpass"
-
-function handleRequest(request, response) {
-  if (request.hasHeader("Authorization")) {
-    if (request.getHeader("Authorization") == "Basic dGVzdHVzZXI6dGVzdHBhc3M=") {
-      response.setStatusLine(request.httpVersion, 302, "Found");
-      response.setHeader("Location", request.queryString);
-      response.write("See " + request.queryString);
-    }
-    else {
-      response.setStatusLine(request.httpVersion, 403, "Forbidden");
-      response.write("Invalid credentials");
-    }
-  }
-  else {
-    response.setStatusLine(request.httpVersion, 401, "Authentication required");
-    response.setHeader("WWW-Authenticate", "basic realm=\"XPInstall\"", false);
-    response.write("Unauthenticed request");
-  }
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_auth.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install succeeds when authentication is required
-// This verifies bug 312473
-function test() {
-  Harness.authenticationCallback = get_auth_info;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_auth_info() {
-  return [ "testuser", "testpass" ];
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
-                          .getService(Components.interfaces.nsIHttpAuthManager);
-  authMgr.clearAll();
-
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_auth2.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when authentication is required and bad
-// credentials are given
-// This verifies bug 312473
-function test() {
-  Harness.authenticationCallback = get_auth_info;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_auth_info() {
-  return [ "baduser", "badpass" ];
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -228, "Install should fail");
-}
-
-function finish_test() {
-  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
-                          .getService(Components.interfaces.nsIHttpAuthManager);
-  authMgr.clearAll();
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_auth3.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when authentication is required and it is
-// canceled
-// This verifies bug 312473
-function test() {
-  Harness.authenticationCallback = get_auth_info;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function get_auth_info() {
-  return null;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -228, "Install should fail");
-}
-
-function finish_test() {
-  var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
-                          .getService(Components.interfaces.nsIHttpAuthManager);
-  authMgr.clearAll();
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_badhash.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when an invalid hash is included
-// This verifies bug 302284
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": {
-      URL: TESTROOT + "unsigned.xpi",
-      Hash: "sha1:643b08418599ddbd1ea8a511c90696578fb844b9",
-      toString: function() { return this.URL; }
-    }
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -261, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_badhashtype.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when an unknown hash type is included
-// This verifies bug 302284
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": {
-      URL: TESTROOT + "unsigned.xpi",
-      Hash: "foo:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
-      toString: function() { return this.URL; }
-    }
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -261, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_bug540558.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that calling InstallTrigger.installChrome works
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "bug540558.html");
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_cancel.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that cancelling an in progress download works.
-var gManager = null;
-
-function test() {
-  waitForExplicitFinish();
-  gManager = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
-                       .createInstance(Components.interfaces.nsIXPInstallManager);
-  gManager.initManagerFromChrome([ TESTROOT + "unsigned.xpi" ],
-                                 1, listener);
-}
-
-function finish_test() {
-  finish();
-}
-
-var listener = {
-  onStateChange: function(index, state, value) {
-    is(index, 0, "There is only one download");
-    if (state == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE)
-      is(value, -210, "Install should have been cancelled");
-    else if (state == Components.interfaces.nsIXPIProgressDialog.DIALOG_CLOSE)
-      finish_test();
-  },
-
-  onProgress: function(index, value, maxValue) {
-    is(index, 0, "There is only one download");
-    gManager.QueryInterface(Components.interfaces.nsIObserver);
-    gManager.observe(null, "xpinstall-progress", "cancel");
-  },
-
-  QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIXPIProgressDialog) ||
-        iid.equals(Components.interfaces.nsISupports))
-      return this;
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
-};
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_chrome.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that starting a download from chrome works and bypasses the whitelist
-function test() {
-  waitForExplicitFinish();
-  var xpimgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
-                         .createInstance(Components.interfaces.nsIXPInstallManager);
-  xpimgr.initManagerFromChrome([ TESTROOT + "unsigned.xpi" ],
-                               1, listener);
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  finish();
-}
-
-var listener = {
-  onStateChange: function(index, state, value) {
-    is(index, 0, "There is only one download");
-    if (state == Components.interfaces.nsIXPIProgressDialog.INSTALL_DONE)
-      is(value, 0, "Install should have succeeded");
-    else if (state == Components.interfaces.nsIXPIProgressDialog.DIALOG_CLOSE)
-      finish_test();
-  },
-
-  onProgress: function(index, value, maxValue) {
-    is(index, 0, "There is only one download");
-  },
-
-  QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIXPIProgressDialog) ||
-        iid.equals(Components.interfaces.nsISupports))
-      return this;
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
-};
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_cookies.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test that an install that requires cookies to be sent fails when no cookies
-// are set
-// This verifies bug 462739
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -228, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_cookies2.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test that an install that requires cookies to be sent succeeds when cookies
-// are set
-// This verifies bug 462739
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
-         false, true, (Date.now() / 1000) + 60);
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_cookies3.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test that an install that requires cookies to be sent succeeds when cookies
-// are set and third party cookies are disabled.
-// This verifies bug 462739
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
-         false, true, (Date.now() / 1000) + 60);
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Cookie check": TESTROOT + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
-
-  Services.prefs.clearUserPref("network.cookie.cookieBehavior");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_cookies4.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test that an install that requires cookies to be sent fails when cookies
-// are set and third party cookies are disabled and the request is to a third
-// party.
-// This verifies bug 462739
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.add("example.com", "/browser/xpinstall/tests", "xpinstall", "true", false,
-         false, true, (Date.now() / 1000) + 60);
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Cookie check": TESTROOT2 + "cookieRedirect.sjs?" + TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -228, "Install should fail");
-}
-
-function finish_test() {
-  var cm = Components.classes["@mozilla.org/cookiemanager;1"]
-                     .getService(Components.interfaces.nsICookieManager2);
-  cm.remove("example.com", "xpinstall", "/browser/xpinstall/tests", false);
-
-  Services.prefs.clearUserPref("network.cookie.cookieBehavior");
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_corrupt.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when the xpi is corrupt.
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Corrupt XPI": TESTROOT + "corrupt.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -207, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("status").textContent, "-207", "Callback should have seen the failure");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_empty.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails when there is no install script present.
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Empty XPI": TESTROOT + "empty.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -204, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_enabled.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an InstallTrigger.enabled is working
-function test() {
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    // Allow the in-page load handler to run first
-    executeSoon(page_loaded);
-  }, true);
-  gBrowser.loadURI(TESTROOT + "enabled.html");
-}
-
-function page_loaded() {
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("enabled").textContent, "true", "installTrigger should have been enabled");
-  gBrowser.removeCurrentTab();
-  finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_enabled2.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an InstallTrigger.enabled is working
-function test() {
-  waitForExplicitFinish();
-
-  Services.prefs.setBoolPref("xpinstall.enabled", false);
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    // Allow the in-page load handler to run first
-    executeSoon(page_loaded);
-  }, true);
-  gBrowser.loadURI(TESTROOT + "enabled.html");
-}
-
-function page_loaded() {
-  Services.prefs.clearUserPref("xpinstall.enabled");
-
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("enabled").textContent, "false", "installTrigger should have not been enabled");
-  gBrowser.removeCurrentTab();
-  finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_enabled3.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an InstallTrigger.install call fails when xpinstall is disabled
-function test() {
-  waitForExplicitFinish();
-
-  Services.prefs.setBoolPref("xpinstall.enabled", false);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    // Allow the in-page load handler to run first
-    executeSoon(page_loaded);
-  }, true);
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function page_loaded() {
-  Services.prefs.clearUserPref("xpinstall.enabled");
-
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("return").textContent, "false", "installTrigger should have not been enabled");
-  gBrowser.removeCurrentTab();
-  finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_hash.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install succeeds when a valid hash is included
-// This verifies bug 302284
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": {
-      URL: TESTROOT + "unsigned.xpi",
-      Hash: "sha1:3d0dc22e1f394e159b08aaf5f0f97de4d5c65f4f",
-      toString: function() { return this.URL; }
-    }
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_installchrome.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that calling InstallTrigger.installChrome works
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_localfile.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an local file works when loading the url
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
-                     .getService(Components.interfaces.nsIChromeRegistry);
-  var path = cr.convertChromeURL(makeURI(CHROMEROOT + "unsigned.xpi")).spec;
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(path);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_localfile2.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install fails if the url is a local file when requested from
-// web content
-function test() {
-  waitForExplicitFinish();
-
-  var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
-                     .getService(Components.interfaces.nsIChromeRegistry);
-  var path = cr.convertChromeURL(makeURI(CHROMEROOT + "unsigned.xpi")).spec;
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": path
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function() {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    // Allow the in-page load handler to run first
-    executeSoon(page_loaded);
-  }, true);
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function page_loaded() {
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("return").textContent, "exception", "installTrigger should have failed");
-  gBrowser.removeCurrentTab();
-  finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_navigateaway.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that navigating away from the initiating page during the install
-// doesn't break the install.
-// This verifies bug 473060
-function test() {
-  Harness.downloadProgressCallback = download_progress;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function download_progress(addon, value, maxValue) {
-  gBrowser.loadURI("about:blank");
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_navigateaway2.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that closing the initiating page during the install doesn't break the
-// install.
-// This verifies bugs 473060 and 475347
-function test() {
-  Harness.downloadProgressCallback = download_progress;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function download_progress(addon, value, maxValue) {
-  gBrowser.removeCurrentTab();
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_offline.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that going offline cancels an in progress download.
-function test() {
-  Harness.downloadProgressCallback = download_progress;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function download_progress(addon, value, maxValue) {
-  try {
-    Services.io.manageOfflineStatus = false;
-    Services.prefs.setBoolPref("browser.offline", true);
-    Services.io.offline = true;
-  } catch (ex) {
-  }
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -210, "Install should be cancelled");
-}
-
-function finish_test() {
-  try {
-    Services.prefs.setBoolPref("browser.offline", false);
-    Services.io.offline = false;
-  } catch (ex) {
-  }
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_opendialog.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Test whether an install succeeds when the progress dialog is already open.
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  window.openDialog("chrome://mozapps/content/extensions/extensions.xul", "",
-                    "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable");
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_multiple.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing two signed add-ons in the same trigger works.
-// This verifies bug 453545
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Signed XPI": TESTROOT + "signed.xpi",
-    "Signed XPI 2": TESTROOT + "signed2.xpi",
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 2, "Should be 2 items listed in the confirmation dialog");
-  is(items[0].name, "Signed XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  is(items[1].name, "Signed XPI 2", "Should have seen the name from the trigger list");
-  is(items[1].url, TESTROOT + "signed2.xpi", "Should have listed the correct url for the item");
-  is(items[1].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[1].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Installs should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
-  em.cancelInstallItem("signed-xpi2@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_naming.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that the correct signer is presented for combinations of O and CN present.
-// The signed files have (when present) O=Mozilla Testing, CN=Object Signer
-// This verifies bug 372980
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Signed XPI (O and CN)": TESTROOT + "signed.xpi",
-    "Signed XPI (CN)": TESTROOT + "signed-no-o.xpi",
-    "Signed XPI (O)": TESTROOT + "signed-no-cn.xpi",
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 3, "Should be 3 items listed in the confirmation dialog");
-  is(items[0].name, "Signed XPI (O and CN)", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  is(items[1].name, "Signed XPI (CN)", "Should have seen the name from the trigger list");
-  is(items[1].url, TESTROOT + "signed-no-o.xpi", "Should have listed the correct url for the item");
-  is(items[1].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[1].signed, "true", "Should have listed the item as signed");
-  is(items[2].name, "Signed XPI (O)", "Should have seen the name from the trigger list");
-  is(items[2].url, TESTROOT + "signed-no-cn.xpi", "Should have listed the correct url for the item");
-  is(items[2].cert, "(Mozilla Testing)", "Should have seen the signer");
-  is(items[2].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Installs should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
-  em.cancelInstallItem("signed-xpi-no-o@tests.mozilla.org");
-  em.cancelInstallItem("signed-xpi-no-cn@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_tampered.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing a signed add-on that has been tampered with after signing.
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Tampered Signed XPI": TESTROOT + "signed-tampered.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "Tampered Signed XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "signed-tampered.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -260, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_trigger.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an signed add-on through an InstallTrigger call in web
-// content.
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Signed XPI": TESTROOT + "signed.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "Signed XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_untrusted.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an add-on signed by an untrusted certificate through an
-// InstallTrigger call in web content.
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Untrusted Signed XPI": TESTROOT + "signed-untrusted.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "Untrusted Signed XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "signed-untrusted.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Unknown Signer)", "Should have seen the supposed signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, -260, "Install should fail");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_signed_url.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an signed add-on by navigating directly to the url
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "signed.xpi");
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "signed.xpi", "Should have had the filename for the item name");
-  is(items[0].url, TESTROOT + "signed.xpi", "Should have listed the correct url for the item");
-  is(items[0].cert, "(Object Signer)", "Should have seen the signer");
-  is(items[0].signed, "true", "Should have listed the item as signed");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("signed-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_softwareupdate.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests that calling InstallTrigger.startSoftwareUpdate works
-function test() {
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_unsigned_trigger.js
+++ /dev/null
@@ -1,58 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through an InstallTrigger call in web
-// content.
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": {
-      URL: TESTROOT + "unsigned.xpi",
-      IconURL: TESTROOT + "icon.png",
-      toString: function() { return this.URL; }
-    }
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "Unsigned XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
-  is(items[0].icon, TESTROOT + "icon.png", "Should have listed the correct icon for the item");
-  is(items[0].signed, "false", "Should have listed the item as unsigned");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  Services.perms.remove("example.com", "install");
-
-  var doc = gBrowser.contentDocument;
-  is(doc.getElementById("return").textContent, "true", "installTrigger should have claimed success");
-  is(doc.getElementById("status").textContent, "0", "Callback should have seen a success");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_unsigned_url.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on by navigating directly to the url
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "unsigned.xpi");
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "unsigned.xpi", "Should have had the filename for the item name");
-  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
-  is(items[0].icon, "", "Should have listed no icon for the item");
-  is(items[0].signed, "false", "Should have listed the item as unsigned");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through an InstallTrigger call in web
-// content. This should be blocked by the whitelist check.
-// This verifies bug 252830
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installBlockedCallback = allow_blocked;
-  Harness.installEndedCallback = check_xpi_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
-  is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
-  return true;
-}
-
-function confirm_install(window) {
-  items = window.document.getElementById("itemList").childNodes;
-  is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
-  is(items[0].name, "Unsigned XPI", "Should have seen the name from the trigger list");
-  is(items[0].url, TESTROOT + "unsigned.xpi", "Should have listed the correct url for the item");
-  is(items[0].signed, "false", "Should have listed the item as unsigned");
-  return true;
-}
-
-function check_xpi_install(addon, status) {
-  is(status, 0, "Install should succeed");
-}
-
-function finish_test() {
-  var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                     .getService(Components.interfaces.nsIExtensionManager);
-  em.cancelInstallItem("unsigned-xpi@tests.mozilla.org");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist2.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through an InstallTrigger call in web
-// content. This should be blocked by the whitelist check because the source
-// is not whitelisted, even though the target is.
-function test() {
-  Harness.installBlockedCallback = allow_blocked;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
-}
-
-function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
-  is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
-  return false;
-}
-
-function finish_test() {
-  Services.perms.remove("example.org", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist3.js
+++ /dev/null
@@ -1,35 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through a navigation. Should not be
-// blocked since the referer is whitelisted.
-function test() {
-  Harness.installConfirmCallback = confirm_install;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "unsigned.xpi", makeURI(TESTROOT2 + "test.html"));
-}
-
-function confirm_install(window) {
-  return false;
-}
-
-function finish_test() {
-  Services.perms.remove("example.org", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist4.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through a navigation. Should be
-// blocked since the referer is not whitelisted even though the target is.
-function test() {
-  Harness.installBlockedCallback = allow_blocked;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  var pm = Services.perms;
-  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
-
-  var triggers = encodeURIComponent(JSON.stringify({
-    "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
-  }));
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "unsigned.xpi", makeURI(TESTROOT2 + "test.html"));
-}
-
-function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
-  is(installInfo.originatingURI.spec, TESTROOT2 + "test.html", "Install should have been triggered by the right uri");
-  return false;
-}
-
-function finish_test() {
-  Services.perms.remove("example.com", "install");
-
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist5.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through a startSoftwareUpdate call in web
-// content. This should be blocked by the whitelist check.
-// This verifies bug 252830
-function test() {
-  Harness.installBlockedCallback = allow_blocked;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
-}
-
-function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
-  is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
-  return false;
-}
-
-function finish_test() {
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/browser_whitelist6.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Load in the test harness
-var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
-                             .getService(Components.interfaces.mozIJSSubScriptLoader);
-
-var rootDir = getRootDirectory(window.location.href);
-scriptLoader.loadSubScript(rootDir + "harness.js", this);
-
-// ----------------------------------------------------------------------------
-// Tests installing an unsigned add-on through an installChrome call in web
-// content. This should be blocked by the whitelist check.
-// This verifies bug 252830
-function test() {
-  Harness.installBlockedCallback = allow_blocked;
-  Harness.installsCompletedCallback = finish_test;
-  Harness.setup();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
-}
-
-function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
-  is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
-  return false;
-}
-
-function finish_test() {
-  gBrowser.removeCurrentTab();
-  Harness.finish();
-}
-// ----------------------------------------------------------------------------
deleted file mode 100644
--- a/xpinstall/tests/bug540558.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<!-- This page tests that window.InstallTrigger.install works -->
-
-<head>
-<title>InstallTrigger tests</title>
-<script type="text/javascript">
-function startInstall() {
-  window.InstallTrigger.install({
-    "Unsigned XPI": "http://example.com/browser/xpinstall/tests/unsigned.xpi"
-  });
-}
-</script>
-</head>
-<body onload="startInstall()">
-<p>InstallTrigger tests</p>
-<p id="return"></p>
-<p id="status"></p>
-</body>
-</html>
deleted file mode 100644
--- a/xpinstall/tests/cookieRedirect.sjs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Simple script redirects to the query part of the uri if the cookie "xpinstall"
-// has the value "true", otherwise gives a 500 error.
-
-function handleRequest(request, response)
-{
-  let cookie = null;
-  if (request.hasHeader("Cookie")) {
-    let cookies = request.getHeader("Cookie").split(";");
-    for (let i = 0; i < cookies.length; i++) {
-      if (cookies[i].substring(0, 10) == "xpinstall=")
-        cookie = cookies[i].substring(10);
-    }
-  }
-
-  if (cookie == "true") {
-    response.setStatusLine(request.httpVersion, 302, "Found");
-    response.setHeader("Location", request.queryString);
-    response.write("See " + request.queryString);
-  }
-  else {
-    response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
-    response.write("Invalid request");
-  }
-}
deleted file mode 100644
--- a/xpinstall/tests/corrupt.xpi
+++ /dev/null
@@ -1,1 +0,0 @@
-This is a corrupt zip file
deleted file mode 100644
index 74ed2b817426d02c0bf2313ed0f2ac728d354800..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/xpinstall/tests/enabled.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<!-- This page will test if InstallTrigger seems to be enabled -->
-
-<head>
-<title>InstallTrigger tests</title>
-<script type="text/javascript">
-function init() {
-  document.getElementById("enabled").textContent = InstallTrigger.enabled() ? "true" : "false";
-}
-</script>
-</head>
-<body onload="init()">
-<p>InstallTrigger tests</p>
-<p id="enabled"></p>
-</body>
-</html>
deleted file mode 100644
--- a/xpinstall/tests/harness.js
+++ /dev/null
@@ -1,246 +0,0 @@
-const TESTROOT = "http://example.com/browser/xpinstall/tests/";
-const TESTROOT2 = "http://example.org/browser/xpinstall/tests/";
-const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
-const PROMPT_URL = "chrome://global/content/commonDialog.xul";
-const ADDONS_URL = "chrome://mozapps/content/extensions/extensions.xul";
-
-var rootDir = getRootDirectory(gTestPath);
-var path = rootDir.split('/');
-var chromeName = path[0] + '//' + path[2];
-var croot = chromeName + "/content/browser/xpinstall/tests/";
-var jar = getJar(croot);
-if (jar) {
-  var tmpdir = extractJarToTmp(jar);
-  croot = 'file://' + tmpdir.path + '/';
-}
-const CHROMEROOT = croot;
-
-/**
- * This is a test harness designed to handle responding to UI during the process
- * of installing an XPI. A test can set callbacks to hear about specific parts
- * of the sequence.
- * Before use setup must be called and finish must be called afterwards.
- */
-var Harness = {
-  // If set then the install is expected to be blocked by the whitelist. The
-  // callback should return true to continue with the install anyway.
-  installBlockedCallback: null,
-  // If set will be called in the event of authentication being needed to get
-  // the xpi. Should return a 2 element array of username and password, or
-  // null to not authenticate.
-  authenticationCallback: null,
-  // If set this will be called to allow checking the contents of the xpinstall
-  // confirmation dialog. The callback should return true to continue the install.
-  installConfirmCallback: null,
-  // If set will be called when downloading of an item has begun.
-  downloadStartedCallback: null,
-  // If set will be called during the download of an item.
-  downloadProgressCallback: null,
-  // If set will be called when downloading of an item has ended.
-  downloadEndedCallback: null,
-  // If set will be called when installation by the extension manager of an xpi
-  // item starts
-  installStartedCallback: null,
-  // If set will be called when each xpi item to be installed completes
-  // installation.
-  installEndedCallback: null,
-  // If set will be called when all triggered items are installed or the install
-  // is canceled.
-  installsCompletedCallback: null,
-
-  listenerIndex: null,
-
-  // Setup and tear down functions
-  setup: function() {
-    waitForExplicitFinish();
-
-    var os = Components.classes["@mozilla.org/observer-service;1"]
-                       .getService(Components.interfaces.nsIObserverService);
-    os.addObserver(this, "xpinstall-install-blocked", false);
-
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                       .getService(Components.interfaces.nsIWindowMediator);
-    wm.addListener(this);
-
-    var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                       .getService(Components.interfaces.nsIExtensionManager);
-    this.listenerIndex = em.addInstallListener(this);
-  },
-
-  finish: function() {
-    var os = Components.classes["@mozilla.org/observer-service;1"]
-                       .getService(Components.interfaces.nsIObserverService);
-    os.removeObserver(this, "xpinstall-install-blocked");
-
-    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                       .getService(Components.interfaces.nsIWindowMediator);
-    wm.removeListener(this);
-    var win = wm.getMostRecentWindow("Extension:Manager");
-    if (win)
-      win.close();
-
-    var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                       .getService(Components.interfaces.nsIExtensionManager);
-    em.removeInstallListenerAt(this.listenerIndex);
-    finish();
-  },
-
-  endTest: function() {
-    // Defer the final notification to allow things like the InstallTrigger
-    // callback to complete
-    executeSoon(this.installsCompletedCallback);
-
-    this.installBlockedCallback = null;
-    this.authenticationCallback = null;
-    this.installConfirmCallback = null;
-    this.downloadStartedCallback = null;
-    this.downloadProgressCallback = null;
-    this.downloadEndedCallback = null;
-    this.installStartedCallback = null;
-    this.installEndedCallback = null;
-    this.installsCompletedCallback = null;
-  },
-
-  // Window open handling
-  windowLoad: function(window) {
-    // Allow any other load handlers to execute
-    var self = this;
-    executeSoon(function() { self.windowReady(window); } );
-  },
-
-  windowReady: function(window) {
-    if (window.document.location.href == XPINSTALL_URL) {
-      if (this.installBlockedCallback)
-        ok(false, "Should have been blocked by the whitelist");
-
-      // If there is a confirm callback then its return status determines whether
-      // to install the items or not. If not the test is over.
-      if (this.installConfirmCallback && !this.installConfirmCallback(window)) {
-        window.document.documentElement.cancelDialog();
-        this.endTest();
-      }
-      else {
-        // Initially the accept button is disabled on a countdown timer
-        var button = window.document.documentElement.getButton("accept");
-        button.disabled = false;
-        window.document.documentElement.acceptDialog();
-      }
-    }
-    else if (window.document.location.href == PROMPT_URL) {
-      switch (window.args.promptType) {
-        default:
-                if (window.opener.document.location.href == ADDONS_URL) {
-                  // A prompt opened by the add-ons manager is liable to be an
-                  // xpinstall error, just close it, we'll see the error in
-                  // onInstallEnded anyway.
-                  window.document.documentElement.acceptDialog();
-                }
-                break;
-        case "promptUserAndPass":
-                  // This is a login dialog, hopefully an authentication prompt
-                  // for the xpi.
-                  if (this.authenticationCallback) {
-                    var auth = this.authenticationCallback();
-                    if (auth && auth.length == 2) {
-                      window.document.getElementById("loginTextbox").value = auth[0];
-                      window.document.getElementById("password1Textbox").value = auth[1];
-                      window.document.documentElement.acceptDialog();
-                    }
-                    else {
-                      window.document.documentElement.cancelDialog();
-                    }
-                  }
-                  else {
-                    window.document.documentElement.cancelDialog();
-                  }
-                break;
-      }
-    }
-  },
-
-  // Install blocked handling
-
-  installBlocked: function(installInfo) {
-    ok(!!this.installBlockedCallback, "Shouldn't have been blocked by the whitelist");
-    if (this.installBlockedCallback && this.installBlockedCallback(installInfo)) {
-      this.installBlockedCallback = null;
-      var mgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"]
-                          .createInstance(Components.interfaces.nsIXPInstallManager);
-      mgr.initManagerWithInstallInfo(installInfo);
-    }
-    else {
-      this.endTest();
-    }
-  },
-
-  // nsIWindowMediatorListener
-
-  onWindowTitleChange: function(window, title) {
-  },
-
-  onOpenWindow: function(window) {
-    var domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                          .getInterface(Components.interfaces.nsIDOMWindow);
-    var self = this;
-    domwindow.addEventListener("load", function() {
-      self.windowLoad(domwindow);
-    }, false);
-  },
-
-  onCloseWindow: function(window) {
-  },
-
-  // nsIAddonInstallListener
-
-  onDownloadStarted: function(addon) {
-    if (this.downloadStartedCallback)
-      this.downloadStartedCallback(addon);
-  },
-
-  onDownloadProgress: function(addon, value, maxValue) {
-    if (this.downloadProgressCallback)
-      this.downloadProgressCallback(addon, value, maxValue);
-  },
-
-  onDownloadEnded: function(addon) {
-    if (this.downloadEndedCallback)
-      this.downloadEndedCallback(addon);
-  },
-
-  onInstallStarted: function(addon) {
-    if (this.installStartedCallback)
-      this.installStartedCallback(addon);
-  },
-
-  onCompatibilityCheckStarted: function(addon) {
-  },
-
-  onCompatibilityCheckEnded: function(addon, status) {
-  },
-
-  onInstallEnded: function(addon, status) {
-    if (this.installEndedCallback)
-      this.installEndedCallback(addon, status);
-  },
-
-  onInstallsCompleted: function() {
-    this.endTest();
-  },
-
-  // nsIObserver
-
-  observe: function(subject, topic, data) {
-    var installInfo = subject.QueryInterface(Components.interfaces.nsIXPIInstallInfo);
-    this.installBlocked(installInfo);
-  },
-
-  QueryInterface: function(iid) {
-    if (iid.equals(Components.interfaces.nsIObserver) ||
-        iid.equals(Components.interfaces.nsIAddonInstallListener) ||
-        iid.equals(Components.interfaces.nsIWindowMediatorListener) ||
-        iid.equals(Components.interfaces.nsISupports))
-      return this;
-
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  }
-}
deleted file mode 100644
--- a/xpinstall/tests/installchrome.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<!-- This page will accept a url as the uri query and pass it to InstallTrigger.installChrome -->
-
-<head>
-<title>InstallTrigger tests</title>
-<script type="text/javascript">
-function startInstall() {
-  InstallTrigger.installChrome(InstallTrigger.SKIN,
-                               decodeURIComponent(document.location.search.substring(1)),
-                               "test");
-}
-</script>
-</head>
-<body onload="startInstall()">
-<p>InstallTrigger tests</p>
-</body>
-</html>
deleted file mode 100644
--- a/xpinstall/tests/installtrigger.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<!-- This page will accept some json as the uri query and pass it to InstallTrigger.install -->
-
-<head>
-<title>InstallTrigger tests</title>
-<script type="text/javascript">
-function installCallback(url, status) {
-  document.getElementById("status").textContent = status;
-}
-
-function startInstall() {
-  var text = decodeURIComponent(document.location.search.substring(1));
-  var triggers = JSON.parse(text);
-  try {
-    document.getElementById("return").textContent = InstallTrigger.install(triggers, installCallback);
-  }
-  catch (e) {
-    document.getElementById("return").textContent = "exception";
-  }
-}
-</script>
-</head>
-<body onload="startInstall()">
-<p>InstallTrigger tests</p>
-<p id="return"></p>
-<p id="status"></p>
-</body>
-</html>
deleted file mode 100644
index 90d3a3ce66692496ee625b2ae1725785193079a5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 19b754038008ed3043a7178aa8c42da83747bf8e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 8c951881e5fbe10504eee8280dd9a3d995b5c881..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 09789d1897ccd3c6e0d30c94dc658f5cda732771..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index bd7f78b7c7712c0aca18776e08541e87898da119..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 085efbbf7fdd8f9cf475e903b16552e614f9139c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/xpinstall/tests/startsoftwareupdate.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html>
-
-<!-- This page will accept a url as the uri query and pass it to InstallTrigger.startSoftwareUpdate -->
-
-<head>
-<title>InstallTrigger tests</title>
-<script type="text/javascript">
-function startInstall() {
-  InstallTrigger.startSoftwareUpdate(decodeURIComponent(document.location.search.substring(1)));
-}
-</script>
-</head>
-<body onload="startInstall()">
-<p>InstallTrigger tests</p>
-</body>
-</html>
deleted file mode 100644
index 51b00475a9641ea9d608874a3ab7679da3a4374b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001