Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 14 Jul 2014 15:47:36 -0400
changeset 215759 2a22da38a185ad0421a438bd75d29c566836664a
parent 215719 bf09d63db425d0be5d969b25714d79d767898bcd (current diff)
parent 215758 ab2b0fb5cfff0bb9ec608abb6d28c9ffee75157a (diff)
child 215811 2bb155ee786b42bcdee7b4266dc9d32dc8bfc971
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.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 inbound to m-c. a=merge
js/src/gc/GCRuntime.h
js/src/gc/Heap.h
js/src/jsgc.cpp
--- a/browser/devtools/scratchpad/test/browser_scratchpad_display_non_error_exceptions.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_display_non_error_exceptions.js
@@ -58,17 +58,18 @@ function runTests()
     result: error3 + openComment + "Exception: [object Object]" + closeComment,
     label: "thrown object display output"
   },
   {
     // Display error4, document.body.appendChild(document.body)
     method: "display",
     code: error4,
     result: error4 + openComment + "Exception: Node cannot be inserted " +
-            "at the specified point in the hierarchy\n@1" + closeComment,
+            "at the specified point in the hierarchy\n@" +
+            scratchpad.uniqueName + ":1:0" + closeComment,
     label: "Alternative format error display output"
   },
   {
     // Run message
     method: "run",
     code: message,
     result: message,
     label: "message run output"
@@ -95,14 +96,15 @@ function runTests()
     result: error3 + openComment + "Exception: [object Object]" + closeComment,
     label: "thrown object run output"
   },
   {
     // Run error4, document.body.appendChild(document.body)
     method: "run",
     code: error4,
     result: error4 + openComment + "Exception: Node cannot be inserted " +
-            "at the specified point in the hierarchy\n@1" + closeComment,
+            "at the specified point in the hierarchy\n@" +
+            scratchpad.uniqueName + ":1:0" + closeComment,
     label: "Alternative format error run output"
   }];
 
   runAsyncTests(scratchpad, tests).then(finish);
 }
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -15,16 +15,21 @@ page {
   background-color: white;
   color: #424E5A;
 }
 
 * {
   -moz-user-select: text;
 }
 
+treecol {
+  /* override the * rule to let the treecol be sortable */
+  -moz-user-select: none;
+}
+
 caption {
   -moz-appearance: none;
   margin: 0;
 }
 
 caption > label {
   font-size: 1.3rem;
   font-weight: bold;
--- a/content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -68,17 +68,17 @@ FFmpegRuntimeLinker::Link()
   for (size_t i = 0; i < ArrayLength(sLibs); i++) {
     FFMPEG_LOG("%s %s", i ? "," : "", sLibs[i].Name);
   }
   FFMPEG_LOG(" ]\n");
 
   Unlink();
 
   sLinkStatus = LinkStatus_FAILED;
-  return nullptr;
+  return false;
 }
 
 /* static */ bool
 FFmpegRuntimeLinker::Bind(const char* aLibName)
 {
 #define AV_FUNC(func)                                                          \
   if (!(func = (typeof(func))dlsym(sLinkedLib, #func))) {                      \
     FFMPEG_LOG("Couldn't load function " #func " from %s.", aLibName);         \
--- a/docshell/test/browser/browser_uriFixupIntegration.js
+++ b/docshell/test/browser/browser_uriFixupIntegration.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 const kSearchEngineID = "browser_urifixup_search_engine";
-const kTest
 const kSearchEngineURL = "http://example.com/?search={searchTerms}";
 Services.search.addEngineWithDetails(kSearchEngineID, "", "", "", "get",
                                      kSearchEngineURL);
 
 let oldDefaultEngine = Services.search.defaultEngine;
 Services.search.defaultEngine = Services.search.getEngineByName(kSearchEngineID);
 
 let tab;
--- a/dom/apps/tests/file_packaged_app.sjs
+++ b/dom/apps/tests/file_packaged_app.sjs
@@ -15,45 +15,33 @@ var gMiniManifestTemplate = "file_packag
 var gAppTemplate = "file_packaged_app.template.html";
 var gAppName = "appname";
 var gDevName = "devname";
 var gDevUrl = "http://dev.url";
 
 function handleRequest(request, response) {
   var query = getQuery(request);
 
+  response.setHeader("Access-Control-Allow-Origin", "*", false);
+
   var packageSize = ("packageSize" in query) ? query.packageSize : 0;
   var appName = ("appName" in query) ? query.appName : gAppName;
   var devName = ("devName" in query) ? query.devName : gDevName;
   var devUrl = ("devUrl" in query) ? query.devUrl : gDevUrl;
-  // allowCancel just means deliver the file slowly so we have time to cancel it
-  var allowCancel = "allowCancel" in query;
-  var getPackage = "getPackage" in query;
-  var alreadyDeferred = Number(getState("alreadyDeferred"));
-
-  if (allowCancel && getPackage && !alreadyDeferred) {
-    // Only do this for the actual package delivery.
-    response.processAsync();
-    // And to avoid timer problems, only do this once.
-    setState("alreadyDeferred", "1");
-  }
-
-  response.setHeader("Access-Control-Allow-Origin", "*", false);
 
   // If this is a version update, update state, prepare the manifest,
   // the application package and return.
   if ("setVersion" in query) {
     var version = query.setVersion;
     setState("version", version);
     var packageVersion = ("dontUpdatePackage" in query) ? version - 1 : version;
     var packageName = "test_packaged_app_" + packageVersion + ".zip";
 
     setState("packageName", packageName);
-    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?" +
-                      (allowCancel?"allowCancel&": "") + "getPackage=" +
+    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?getPackage=" +
                       packageName;
     setState("packagePath", packagePath);
 
     if (version == packageVersion) {
       // Create the application package.
       var zipWriter = Cc["@mozilla.org/zipwriter;1"]
                         .createInstance(Ci.nsIZipWriter);
       var zipFile = FileUtils.getFile("TmpD", [packageName]);
@@ -91,29 +79,21 @@ function handleRequest(request, response
     dump("Etags Match. Sending 304\n");
     response.setStatusLine(request.httpVersion, "304", "Not modified");
     return;
   }
 
   response.setHeader("Etag", etag, false);
 
   // Serve the application package corresponding to the requested app version.
-  if (getPackage) {
+  if ("getPackage" in query) {
     var resource = readFile(packageName, true);
     response.setHeader("Content-Type",
                        "Content-Type: application/java-archive", false);
-    if (allowCancel && !alreadyDeferred) {
-      var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      timer.initWithCallback(function (aTimer) {
-          response.write(resource);
-          response.finish();
-      }, 3000, Ci.nsITimer.TYPE_ONE_SHOT);
-    } else {
-      response.write(resource);
-    }
+    response.write(resource);
     return;
   }
 
   // Serve the mini-manifest corresponding to the requested app version.
   if ("getManifest" in query) {
     var template = gBasePath + gMiniManifestTemplate;
     if (!("noManifestContentType" in query)) {
       response.setHeader("Content-Type",
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -52,27 +52,23 @@ var PackagedTestHelper = (function Packa
     finish();
   }
 
   function xhrAbort(url) {
     ok(false, "XHR abort loading " + url);
     finish();
   }
 
-  function setAppVersion(aVersion, aCb, aDontUpdatePackage, aAllowCancel) {
+  function setAppVersion(aVersion, aCb, aDontUpdatePackage) {
     var xhr = new XMLHttpRequest();
     var dontUpdate = "";
-    var allowCancel = "";
     if (aDontUpdatePackage) {
       dontUpdate = "&dontUpdatePackage=1";
     }
-    if (aAllowCancel) {
-      allowCancel= "&allowCancel=1";
-    }
-    var url = gSJS + "?setVersion=" + aVersion + dontUpdate + allowCancel;
+    var url = gSJS + "?setVersion=" + aVersion + dontUpdate;
     xhr.addEventListener("load", function() {
                            is(xhr.responseText, "OK", "setAppVersion OK");
                            aCb();
                          });
     xhr.addEventListener("error", event => xhrError(event, url));
     xhr.addEventListener("abort", event => xhrAbort(url));
     xhr.open("GET", url, true);
     xhr.send();
--- a/dom/apps/tests/test_packaged_app_install.html
+++ b/dom/apps/tests/test_packaged_app_install.html
@@ -29,29 +29,29 @@ function checkAppInstallError(aMiniManif
   var req = navigator.mozApps.installPackage(aMiniManifestURL);
   req.onsuccess = function() {
     ok(false, "We are supposed to throw " + aExpectedError);
     PackagedTestHelper.finish();
   };
   req.onerror = function(evt) {
     var error = evt.target.error.name;
     if (error == aExpectedError) {
-      info("Got expected " + aExpectedError);
+      ok(true, "Got expected " + aExpectedError);
       PackagedTestHelper.next();
     } else {
       ok(false, "Got unexpected " + error);
       PackagedTestHelper.finish();
     }
   };
 }
 
 function checkUninstallApp(aApp) {
   var req = navigator.mozApps.mgmt.uninstall(aApp);
   req.onsuccess = function() {
-    info("App uninstalled");
+    ok(true, "App uninstalled");
     aApp.ondownloadsuccess = null;
     aApp.ondownloaderror = null;
     aApp.onprogress = null;
     PackagedTestHelper.next();
   };
   req.onerror = function(evt) {
     ok(false, "Got unexpected " + evt.target.error.name);
     PackagedTestHelper.finish();
@@ -78,84 +78,84 @@ function checkInstalledApp(aMiniManifest
 
 SimpleTest.waitForExplicitFinish();
 
 var steps = [
   function() {
     // Set up
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.addPermission("webapps-manage", true, document);
-    info("Set up");
+    ok(true, "Set up");
     PackagedTestHelper.next();
   },
   function() {
-    info("autoConfirmAppInstall");
+    ok(true, "autoConfirmAppInstall");
     SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
   },
   function() {
     PackagedTestHelper.setAppVersion(0, PackagedTestHelper.next);
   },
   function() {
     // Bug 927699 - navigator.mozApps.install(url) lets NS_ERROR_FAILURE onto
     //              the web.
-    info("== TEST == INVALID_URL");
+    ok(true, "== TEST == INVALID_URL");
     checkAppInstallError("", "INVALID_URL");
   },
   function() {
     // Test network error.
-    info("== TEST == Network error");
+    ok(true, "== TEST == Network error");
     checkAppInstallError("http://notvalidurl", "NETWORK_ERROR");
   },
   function() {
     // Test wrong mini-manifest content type.
-    info("== TEST == Not valid mini-manifest content type");
+    ok(true, "== TEST == Not valid mini-manifest content type");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&noManifestContentType=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST_CONTENT_TYPE");
   },
   function() {
     // Test mini-manifest 'size' value is not number. Bug 839435.
-    info("== TEST == Size value is not a number");
+    ok(true, "== TEST == Size value is not a number");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=\"NotANumber\"";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test mini-manifest  negative 'size' value. Bug 839435.
-    info("== TEST == Negative size value");
+    ok(true, "== TEST == Negative size value");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=-1";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test wrong package path
-    info("== TEST == Installing app with wrong package path");
+    ok(true, "== TEST == Installing app with wrong package path");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&wrongPackagePath=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test no manifest in zip file.
-    info("== TEST == No manifest in the zip file");
+    ok(true, "== TEST == No manifest in the zip file");
     var miniManifestURL = PackagedTestHelper.gSJS + "?getManifest=true";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                             "MISSING_MANIFEST", 0, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
       PackagedTestHelper.setAppVersion(1, PackagedTestHelper.next);
   },
   function() {
     // Test mini-manifest app name is different from the webapp manifest name.
     // Bug 844243.
-    info("== TEST == Mini-manifest app name is different from webapp " +
+    ok(true, "== TEST == Mini-manifest app name is different from webapp " +
              "manifest name");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&appName=arandomname";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              "arandomname");
   },
@@ -182,29 +182,29 @@ var steps = [
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
     PackagedTestHelper.setAppVersion(2, PackagedTestHelper.next);
   },
   function() {
-    info("== TEST == Install packaged app");
+    ok(true, "== TEST == Install packaged app");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true";
     navigator.mozApps.mgmt.oninstall = function(evt) {
-      info("Got oninstall event");
+      ok(true, "Got oninstall event");
       PackagedTestHelper.gApp = evt.application;
       PackagedTestHelper.gApp.ondownloaderror = function() {
         ok(false, "Download error " +
                   PackagedTestHelper.gApp.downloadError.name);
         PackagedTestHelper.finish();
       };
       PackagedTestHelper.gApp.ondownloadsuccess = function() {
-        info("App downloaded");
+        ok(true, "App downloaded");
         var expected = {
           name: PackagedTestHelper.gAppName,
           manifestURL: miniManifestURL,
           installOrigin: PackagedTestHelper.gInstallOrigin,
           progress: 0,
           installState: "installed",
           downloadAvailable: false,
           downloading: false,
@@ -215,95 +215,21 @@ var steps = [
         PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 2, expected,
                                          true, false, PackagedTestHelper.next);
       };
     };
 
     var request = navigator.mozApps.installPackage(miniManifestURL);
     request.onerror = PackagedTestHelper.mozAppsError;
     request.onsuccess = function() {
-      info("Application installed");
+      ok(true, "Application installed");
     };
   },
   function() {
-    PackagedTestHelper.setAppVersion(3, PackagedTestHelper.next, false, true);
-  },
-  function() {
-    info("== TEST == Install packaged app with a cancel/resume");
-    var miniManifestURL = PackagedTestHelper.gSJS +
-                          "?getManifest=true&allowCancel";
-    navigator.mozApps.mgmt.oninstall = function(evt) {
-      info("Got oninstall event");
-      PackagedTestHelper.gApp = evt.application;
-
-      PackagedTestHelper.gApp.onprogress = function() {
-        // Let's try cancelling and resuming the download later on.
-        PackagedTestHelper.gApp.cancelDownload();
-        // And only do this once.
-        PackagedTestHelper.gApp.onprogress = null;
-      };
-
-      var alreadyCancelled = false;
-      PackagedTestHelper.gApp.ondownloaderror = function() {
-        ok(!alreadyCancelled, "The download should be cancelled only once!");
-        is(PackagedTestHelper.gApp.downloadError.name, "DOWNLOAD_CANCELED",
-           "Download error " + PackagedTestHelper.gApp.downloadError.name);
-        if (!alreadyCancelled) {
-          PackagedTestHelper.gApp.download();
-          alreadyCancelled = true;
-        }
-      };
-
-      PackagedTestHelper.gApp.ondownloadsuccess = function() {
-        info("App downloaded");
-        // We could try also applying the download we just made.
-        var expected = {
-          name: PackagedTestHelper.gAppName,
-          manifestURL: miniManifestURL,
-          installOrigin: PackagedTestHelper.gInstallOrigin,
-          progress: 0,
-          installState: "pending",
-          downloadAvailable: false,
-          downloading: false,
-          downloadSize: 0,
-          size: 0,
-          readyToApplyDownload: true
-        };
-        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
-                                         true, false, function() {});
-      };
-
-      PackagedTestHelper.gApp.ondownloadapplied = function() {
-        info("App download applied.");
-        var expected = {
-          name: PackagedTestHelper.gAppName,
-          manifestURL: miniManifestURL,
-          installOrigin: PackagedTestHelper.gInstallOrigin,
-          progress: 0,
-          installState: "installed",
-          downloadAvailable: false,
-          downloading: false,
-          downloadSize: 0,
-          size: 0,
-          readyToApplyDownload: false
-        };
-        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
-                                         true, false, PackagedTestHelper.next);
-      }
-
-    };
-
-    var request = navigator.mozApps.installPackage(miniManifestURL);
-    request.onerror = PackagedTestHelper.mozAppsError;
-    request.onsuccess = function() {
-      info("Application installed");
-    };
-  },
-  function() {
-    info("all done!\n");
+    ok(true, "all done!\n");
     PackagedTestHelper.finish();
   }
 ];
 
 PackagedTestHelper.setSteps(steps);
 
 addLoadEvent(PackagedTestHelper.start);
 
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -17,16 +17,17 @@
 #include "nsIDOMDOMException.h"
 #include "nsIException.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsMemory.h"
 #include "prprf.h"
 #include "xpcprivate.h"
 
 #include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
 
 enum DOM4ErrorTypeCodeMap {
   /* DOM4 errors from http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */
   IndexSizeError             = nsIDOMDOMException::INDEX_SIZE_ERR,
   HierarchyRequestError      = nsIDOMDOMException::HIERARCHY_REQUEST_ERR,
   WrongDocumentError         = nsIDOMDOMException::WRONG_DOCUMENT_ERR,
@@ -549,16 +550,24 @@ Exception::GetInner() const
 already_AddRefed<nsISupports>
 Exception::GetData() const
 {
   nsCOMPtr<nsISupports> data = mData;
   return data.forget();
 }
 
 void
+Exception::GetStack(nsAString& aStack, ErrorResult& aRv) const
+{
+  if (mLocation) {
+    aRv = mLocation->GetFormattedStack(aStack);
+  }
+}
+
+void
 Exception::Stringify(nsString& retval)
 {
   nsCString str;
 #ifdef DEBUG
   DebugOnly<nsresult> rv =
 #endif
   ToString(str);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -27,16 +27,18 @@ class nsIStackFrame;
 class nsString;
 
 nsresult
 NS_GetNameAndMessageForDOMNSResult(nsresult aNSResult, nsACString& aName,
                                    nsACString& aMessage,
                                    uint16_t* aCode = nullptr);
 
 namespace mozilla {
+class ErrorResult;
+
 namespace dom {
 
 #define MOZILLA_EXCEPTION_IID \
 { 0x55eda557, 0xeba0, 0x4fe3, \
   { 0xae, 0x2e, 0xf3, 0x94, 0x49, 0x23, 0x62, 0xd6 } }
 
 class Exception : public nsIXPCException,
                   public nsWrapperCache
@@ -76,16 +78,18 @@ public:
   uint32_t ColumnNumber() const;
 
   already_AddRefed<nsIStackFrame> GetLocation() const;
 
   already_AddRefed<nsISupports> GetInner() const;
 
   already_AddRefed<nsISupports> GetData() const;
 
+  void GetStack(nsAString& aStack, ErrorResult& aRv) const;
+
   void Stringify(nsString& retval);
 
   // XPCOM factory ctor.
   Exception();
 
   Exception(const nsACString& aMessage,
             nsresult aResult,
             const nsACString& aName,
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -177,282 +177,364 @@ GetCurrentJSStack()
     return nullptr;
   }
 
   nsCOMPtr<nsIStackFrame> stack = exceptions::CreateStack(cx);
   if (!stack) {
     return nullptr;
   }
 
-  // peel off native frames...
-  uint32_t language;
-  nsCOMPtr<nsIStackFrame> caller;
-  while (stack &&
-         NS_SUCCEEDED(stack->GetLanguage(&language)) &&
-         language != nsIProgrammingLanguage::JAVASCRIPT &&
-         NS_SUCCEEDED(stack->GetCaller(getter_AddRefs(caller))) &&
-         caller) {
-    stack = caller;
-  }
+  // Note that CreateStack only returns JS frames, so we're done here.
   return stack.forget();
 }
 
 namespace exceptions {
 
-class StackDescriptionOwner {
-public:
-  StackDescriptionOwner(JS::StackDescription* aDescription)
-    : mDescription(aDescription)
-  {
-    mozilla::HoldJSObjects(this);
-  }
-
-protected:
-  ~StackDescriptionOwner()
-  {
-    // Make sure to set mDescription to null before calling DropJSObjects, since
-    // in debug builds DropJSObjects try to trace us and we don't want to trace
-    // a dead StackDescription.
-    if (mDescription) {
-      JS::FreeStackDescription(nullptr, mDescription);
-      mDescription = nullptr;
-    }
-    mozilla::DropJSObjects(this);
-  }
-
-public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(StackDescriptionOwner)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(StackDescriptionOwner)
-
-  JS::FrameDescription& FrameAt(size_t aIndex)
-  {
-    MOZ_ASSERT(aIndex < mDescription->nframes);
-    return mDescription->frames[aIndex];
-  }
-
-  unsigned NumFrames()
-  {
-    return mDescription->nframes;
-  }
-
-private:
-  JS::StackDescription* mDescription;
-};
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(StackDescriptionOwner, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(StackDescriptionOwner, Release)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(StackDescriptionOwner)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StackDescriptionOwner)
-  if (tmp->mDescription) {
-    JS::FreeStackDescription(nullptr, tmp->mDescription);
-    tmp->mDescription = nullptr;
-  }
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StackDescriptionOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(StackDescriptionOwner)
-  JS::StackDescription* desc = tmp->mDescription;
-  if (tmp->mDescription) {
-    for (size_t i = 0; i < desc->nframes; ++i) {
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation1());
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation2());
-    }
-  }
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-class JSStackFrame : public nsIStackFrame
+class StackFrame : public nsIStackFrame
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(JSStackFrame)
+  NS_DECL_CYCLE_COLLECTION_CLASS(StackFrame)
   NS_DECL_NSISTACKFRAME
 
-  // A null aStackDescription or an aIndex that's out of range for the
-  // number of frames aStackDescription has will mean that the
-  // JSStackFrame will never look at the stack description.  Instead,
-  // it is expected to be initialized by the caller as needed.
-  JSStackFrame(StackDescriptionOwner* aStackDescription, size_t aIndex);
+  StackFrame(uint32_t aLanguage,
+             const char* aFilename,
+             const char* aFunctionName,
+             int32_t aLineNumber,
+             nsIStackFrame* aCaller);
 
-  static already_AddRefed<nsIStackFrame>
-  CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
+  StackFrame()
+    : mLineno(0)
+    , mLanguage(nsIProgrammingLanguage::UNKNOWN)
+  {
+  }
+
   static already_AddRefed<nsIStackFrame>
   CreateStackFrameLocation(uint32_t aLanguage,
                            const char* aFilename,
                            const char* aFunctionName,
                            int32_t aLineNumber,
                            nsIStackFrame* aCaller);
+protected:
+  virtual ~StackFrame();
+
+  virtual bool IsJSFrame() const
+  {
+    return false;
+  }
+
+  virtual nsresult GetLineno(int32_t* aLineNo)
+  {
+    *aLineNo = mLineno;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIStackFrame> mCaller;
+  nsString mFilename;
+  nsString mFunname;
+  int32_t mLineno;
+  uint32_t mLanguage;
+};
+
+StackFrame::StackFrame(uint32_t aLanguage,
+                       const char* aFilename,
+                       const char* aFunctionName,
+                       int32_t aLineNumber,
+                       nsIStackFrame* aCaller)
+  : mCaller(aCaller)
+  , mLineno(aLineNumber)
+  , mLanguage(aLanguage)
+{
+  CopyUTF8toUTF16(aFilename, mFilename);
+  CopyUTF8toUTF16(aFunctionName, mFunname);
+}
+
+StackFrame::~StackFrame()
+{
+}
+
+NS_IMPL_CYCLE_COLLECTION(StackFrame, mCaller)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(StackFrame)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(StackFrame)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StackFrame)
+  NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+class JSStackFrame : public StackFrame
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSStackFrame,
+                                                         StackFrame)
+
+  // aStack must not be null.
+  JSStackFrame(JS::Handle<JSObject*> aStack);
+
+  static already_AddRefed<nsIStackFrame>
+  CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
+
+  NS_IMETHOD GetLanguageName(nsACString& aLanguageName) MOZ_OVERRIDE;
+  NS_IMETHOD GetFilename(nsAString& aFilename) MOZ_OVERRIDE;
+  NS_IMETHOD GetName(nsAString& aFunction) MOZ_OVERRIDE;
+  NS_IMETHOD GetCaller(nsIStackFrame** aCaller) MOZ_OVERRIDE;
+  NS_IMETHOD GetFormattedStack(nsAString& aStack) MOZ_OVERRIDE;
+
+protected:
+  virtual bool IsJSFrame() const MOZ_OVERRIDE {
+    return true;
+  }
+
+  virtual nsresult GetLineno(int32_t* aLineNo) MOZ_OVERRIDE;
 
 private:
   virtual ~JSStackFrame();
 
-  bool IsJSFrame() const {
-    return mLanguage == nsIProgrammingLanguage::JAVASCRIPT;
-  }
-
-  int32_t GetLineno();
-
-  nsRefPtr<StackDescriptionOwner> mStackDescription;
-  nsCOMPtr<nsIStackFrame> mCaller;
-
-  // Cached values
-  nsString mFilename;
-  nsString mFunname;
-  int32_t mLineno;
-  uint32_t mLanguage;
-
-  size_t mIndex;
+  JS::Heap<JSObject*> mStack;
+  nsString mFormattedStack;
 
   bool mFilenameInitialized;
   bool mFunnameInitialized;
   bool mLinenoInitialized;
   bool mCallerInitialized;
+  bool mFormattedStackInitialized;
 };
 
-JSStackFrame::JSStackFrame(StackDescriptionOwner* aStackDescription,
-                           size_t aIndex)
-  : mLineno(0)
+JSStackFrame::JSStackFrame(JS::Handle<JSObject*> aStack)
+  : mStack(aStack)
+  , mFilenameInitialized(false)
+  , mFunnameInitialized(false)
+  , mLinenoInitialized(false)
+  , mCallerInitialized(false)
+  , mFormattedStackInitialized(false)
 {
-  if (aStackDescription && aIndex < aStackDescription->NumFrames()) {
-    mStackDescription = aStackDescription;
-    mIndex = aIndex;
-    mFilenameInitialized = false;
-    mFunnameInitialized = false;
-    mLinenoInitialized = false;
-    mCallerInitialized = false;
-    mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
-  } else {
-    MOZ_ASSERT(!mStackDescription);
-    mIndex = 0;
-    mFilenameInitialized = true;
-    mFunnameInitialized = true;
-    mLinenoInitialized = true;
-    mCallerInitialized = true;
-    mLanguage = nsIProgrammingLanguage::UNKNOWN;
-  }
+  MOZ_ASSERT(mStack);
+
+  mozilla::HoldJSObjects(this);
+  mLineno = 0;
+  mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
 }
 
 JSStackFrame::~JSStackFrame()
 {
+  mozilla::DropJSObjects(this);
 }
 
-NS_IMPL_CYCLE_COLLECTION(JSStackFrame, mStackDescription, mCaller)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(JSStackFrame)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(JSStackFrame)
+NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  tmp->mStack = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSStackFrame)
-  NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
+NS_IMPL_ADDREF_INHERITED(JSStackFrame, StackFrame)
+NS_IMPL_RELEASE_INHERITED(JSStackFrame, StackFrame)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(JSStackFrame)
+NS_INTERFACE_MAP_END_INHERITING(StackFrame)
 
 /* readonly attribute uint32_t language; */
-NS_IMETHODIMP JSStackFrame::GetLanguage(uint32_t* aLanguage)
+NS_IMETHODIMP StackFrame::GetLanguage(uint32_t* aLanguage)
 {
   *aLanguage = mLanguage;
   return NS_OK;
 }
 
 /* readonly attribute string languageName; */
+NS_IMETHODIMP StackFrame::GetLanguageName(nsACString& aLanguageName)
+{
+  aLanguageName.AssignLiteral("C++");
+  return NS_OK;
+}
+
 NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
 {
-  static const char js[] = "JavaScript";
-  static const char cpp[] = "C++";
-
-  if (IsJSFrame()) {
-    aLanguageName.AssignASCII(js);
-  } else {
-    aLanguageName.AssignASCII(cpp);
-  }
-
+  aLanguageName.AssignLiteral("JavaScript");
   return NS_OK;
 }
 
 /* readonly attribute AString filename; */
 NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
 {
   if (!mFilenameInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    if (const char *filename = desc.filename()) {
-      CopyUTF8toUTF16(filename, mFilename);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> filenameVal(cx);
+    if (!JS_GetProperty(cx, stack, "source", &filenameVal) ||
+        !filenameVal.isString()) {
+      return NS_ERROR_UNEXPECTED;
     }
+    nsAutoJSString str;
+    if (!str.init(cx, filenameVal.toString())) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    mFilename = str;
     mFilenameInitialized = true;
   }
 
+  return StackFrame::GetFilename(aFilename);
+}
+
+NS_IMETHODIMP StackFrame::GetFilename(nsAString& aFilename)
+{
   // The filename must be set to null if empty.
   if (mFilename.IsEmpty()) {
     aFilename.SetIsVoid(true);
   } else {
     aFilename.Assign(mFilename);
   }
 
   return NS_OK;
 }
 
 /* readonly attribute AString name; */
 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
 {
   if (!mFunnameInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    if (JSFlatString *name = desc.funDisplayName()) {
-      AssignJSFlatString(mFunname, name);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> nameVal(cx);
+    // functionDisplayName can be null
+    if (!JS_GetProperty(cx, stack, "functionDisplayName", &nameVal) ||
+        (!nameVal.isString() && !nameVal.isNull())) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    if (nameVal.isString()) {
+      nsAutoJSString str;
+      if (!str.init(cx, nameVal.toString())) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      mFunname = str;
     }
     mFunnameInitialized = true;
   }
 
+  return StackFrame::GetName(aFunction);
+}
+
+NS_IMETHODIMP StackFrame::GetName(nsAString& aFunction)
+{
   // The function name must be set to null if empty.
   if (mFunname.IsEmpty()) {
     aFunction.SetIsVoid(true);
   } else {
     aFunction.Assign(mFunname);
   }
 
   return NS_OK;
 }
 
-int32_t
-JSStackFrame::GetLineno()
+// virtual
+nsresult
+JSStackFrame::GetLineno(int32_t* aLineNo)
 {
   if (!mLinenoInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    mLineno = desc.lineno();
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> lineVal(cx);
+    if (!JS_GetProperty(cx, stack, "line", &lineVal) ||
+        !lineVal.isNumber()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    mLineno = lineVal.toNumber();
     mLinenoInitialized = true;
   }
 
-  return mLineno;
+  return StackFrame::GetLineno(aLineNo);
 }
 
 /* readonly attribute int32_t lineNumber; */
-NS_IMETHODIMP JSStackFrame::GetLineNumber(int32_t* aLineNumber)
+NS_IMETHODIMP StackFrame::GetLineNumber(int32_t* aLineNumber)
 {
-  *aLineNumber = GetLineno();
-  return NS_OK;
+  return GetLineno(aLineNumber);
 }
 
 /* readonly attribute AUTF8String sourceLine; */
-NS_IMETHODIMP JSStackFrame::GetSourceLine(nsACString& aSourceLine)
+NS_IMETHODIMP StackFrame::GetSourceLine(nsACString& aSourceLine)
 {
   aSourceLine.Truncate();
   return NS_OK;
 }
 
 /* readonly attribute nsIStackFrame caller; */
 NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller)
 {
   if (!mCallerInitialized) {
-    mCaller = new JSStackFrame(mStackDescription, mIndex+1);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> callerVal(cx);
+    if (!JS_GetProperty(cx, stack, "parent", &callerVal) ||
+        !callerVal.isObjectOrNull()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (callerVal.isObject()) {
+      JS::Rooted<JSObject*> caller(cx, &callerVal.toObject());
+      mCaller = new JSStackFrame(caller);
+    } else {
+      // Do we really need this dummy frame?  If so, we should document why... I
+      // guess for symmetry with the "nothing on the stack" case, which returns
+      // a single dummy frame?
+      mCaller = new StackFrame();
+    }
     mCallerInitialized = true;
   }
+  return StackFrame::GetCaller(aCaller);
+}
+
+NS_IMETHODIMP StackFrame::GetCaller(nsIStackFrame** aCaller)
+{
   NS_IF_ADDREF(*aCaller = mCaller);
   return NS_OK;
 }
 
+NS_IMETHODIMP JSStackFrame::GetFormattedStack(nsAString& aStack)
+{
+  if (!mFormattedStackInitialized) {
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JS::Value> stack(cx, JS::ObjectValue(*mStack));
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, mStack);
+    JS::Rooted<JSString*> formattedStack(cx, JS::ToString(cx, stack));
+    if (!formattedStack) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    nsAutoJSString str;
+    if (!str.init(cx, formattedStack)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    mFormattedStack = str;
+    mFormattedStackInitialized = true;
+  }
+
+  aStack = mFormattedStack;
+  return NS_OK;
+}
+
+NS_IMETHODIMP StackFrame::GetFormattedStack(nsAString& aStack)
+{
+  aStack.Truncate();
+  return NS_OK;
+}
+
 /* AUTF8String toString (); */
-NS_IMETHODIMP JSStackFrame::ToString(nsACString& _retval)
+NS_IMETHODIMP StackFrame::ToString(nsACString& _retval)
 {
   _retval.Truncate();
 
   const char* frametype = IsJSFrame() ? "JS" : "native";
 
   nsString filename;
   nsresult rv = GetFilename(filename);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -463,75 +545,76 @@ NS_IMETHODIMP JSStackFrame::ToString(nsA
 
   nsString funname;
   rv = GetName(funname);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (funname.IsEmpty()) {
     funname.AssignLiteral("<TOP_LEVEL>");
   }
+
+  int32_t lineno;
+  rv = GetLineno(&lineno);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   static const char format[] = "%s frame :: %s :: %s :: line %d";
   _retval.AppendPrintf(format, frametype,
                        NS_ConvertUTF16toUTF8(filename).get(),
                        NS_ConvertUTF16toUTF8(funname).get(),
-                       GetLineno());
+                       lineno);
   return NS_OK;
 }
 
 /* static */ already_AddRefed<nsIStackFrame>
 JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
 {
   static const unsigned MAX_FRAMES = 100;
   if (aMaxDepth < 0) {
     aMaxDepth = MAX_FRAMES;
   }
 
-  JS::StackDescription* desc = JS::DescribeStack(aCx, aMaxDepth);
-  if (!desc) {
+  JS::Rooted<JSObject*> stack(aCx);
+  if (!JS::CaptureCurrentStack(aCx, &stack, aMaxDepth)) {
     return nullptr;
   }
 
-  nsRefPtr<StackDescriptionOwner> descOwner = new StackDescriptionOwner(desc);
-
-  nsRefPtr<JSStackFrame> first = new JSStackFrame(descOwner, 0);
+  nsCOMPtr<nsIStackFrame> first;
+  if (!stack) {
+    first = new StackFrame();
+  } else {
+    first = new JSStackFrame(stack);
+  }
   return first.forget();
 }
 
 /* static */ already_AddRefed<nsIStackFrame>
-JSStackFrame::CreateStackFrameLocation(uint32_t aLanguage,
-                                       const char* aFilename,
-                                       const char* aFunctionName,
-                                       int32_t aLineNumber,
-                                       nsIStackFrame* aCaller)
+StackFrame::CreateStackFrameLocation(uint32_t aLanguage,
+                                     const char* aFilename,
+                                     const char* aFunctionName,
+                                     int32_t aLineNumber,
+                                     nsIStackFrame* aCaller)
 {
-  nsRefPtr<JSStackFrame> self = new JSStackFrame(nullptr, 0);
-
-  self->mLanguage = aLanguage;
-  self->mLineno = aLineNumber;
-  CopyUTF8toUTF16(aFilename, self->mFilename);
-  CopyUTF8toUTF16(aFunctionName, self->mFunname);
-
-  self->mCaller = aCaller;
-
+  nsRefPtr<StackFrame> self =
+    new StackFrame(aLanguage, aFilename, aFunctionName, aLineNumber, aCaller);
   return self.forget();
 }
 
 already_AddRefed<nsIStackFrame>
 CreateStack(JSContext* aCx, int32_t aMaxDepth)
 {
   return JSStackFrame::CreateStack(aCx, aMaxDepth);
 }
 
 already_AddRefed<nsIStackFrame>
 CreateStackFrameLocation(uint32_t aLanguage,
                          const char* aFilename,
                          const char* aFunctionName,
                          int32_t aLineNumber,
                          nsIStackFrame* aCaller)
 {
-  return JSStackFrame::CreateStackFrameLocation(aLanguage, aFilename,
-                                                aFunctionName, aLineNumber,
-                                                aCaller);
+  return StackFrame::CreateStackFrameLocation(aLanguage, aFilename,
+                                              aFunctionName, aLineNumber,
+                                              aCaller);
 }
 
 } // namespace exceptions
 } // namespace dom
 } // namespace mozilla
--- a/dom/identity/DOMIdentity.jsm
+++ b/dom/identity/DOMIdentity.jsm
@@ -1,34 +1,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const {classes: Cc,
+       interfaces: Ci,
+       utils: Cu,
+       results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const PREF_FXA_ENABLED = "identity.fxaccounts.enabled";
 const FXA_PERMISSION = "firefox-accounts";
 
 // This is the parent process corresponding to nsDOMIdentity.
 this.EXPORTED_SYMBOLS = ["DOMIdentity"];
 
 XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
                                   "resource://gre/modules/identity/IdentityUtils.jsm");
 
+/* jshint ignore:start */
 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
 #ifdef MOZ_B2G_VERSION
                                   "resource://gre/modules/identity/MinimalIdentity.jsm");
 #else
                                   "resource://gre/modules/identity/Identity.jsm");
 #endif
+/* jshint ignore:end */
 
 XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts",
                                   "resource://gre/modules/identity/FirefoxAccounts.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
                                   "resource://gre/modules/identity/IdentityUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this,
@@ -46,39 +51,58 @@ XPCOMUtils.defineLazyServiceGetter(this,
 function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
 }
 
 function IDDOMMessage(aOptions) {
   objectCopy(aOptions, this);
 }
 
+function _sendAsyncMessage(identifier, message) {
+  if (this._mm) {
+    try {
+      this._mm.sendAsyncMessage(identifier, message);
+    } catch(err) {
+      // We may receive a NS_ERROR_NOT_INITIALIZED if the target window has
+      // been closed.  This can legitimately happen if an app has been killed
+      // while we are in the midst of a sign-in flow.
+      if (err.result == Cr.NS_ERROR_NOT_INITIALIZED) {
+        log("Cannot sendAsyncMessage because the recipient frame has closed");
+        return;
+      }
+      log("ERROR: sendAsyncMessage: " + err);
+    }
+  }
+};
+
 function IDPProvisioningContext(aID, aOrigin, aTargetMM) {
   this._id = aID;
   this._origin = aOrigin;
   this._mm = aTargetMM;
 }
 
 IDPProvisioningContext.prototype = {
   get id() this._id,
   get origin() this._origin,
 
+  sendAsyncMessage: _sendAsyncMessage,
+
   doBeginProvisioningCallback: function IDPPC_doBeginProvCB(aID, aCertDuration) {
     let message = new IDDOMMessage({id: this.id});
     message.identity = aID;
     message.certDuration = aCertDuration;
-    this._mm.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
-                              message);
+    this.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
+                          message);
   },
 
   doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
     log("doGenKeyPairCallback");
     let message = new IDDOMMessage({id: this.id});
     message.publicKey = aPublicKey;
-    this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
+    this.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
   },
 
   doError: function(msg) {
     log("Provisioning ERROR: " + msg);
   }
 };
 
 function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
@@ -86,21 +110,23 @@ function IDPAuthenticationContext(aID, a
   this._origin = aOrigin;
   this._mm = aTargetMM;
 }
 
 IDPAuthenticationContext.prototype = {
   get id() this._id,
   get origin() this._origin,
 
+  sendAsyncMessage: _sendAsyncMessage,
+
   doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) {
     let message = new IDDOMMessage({id: this.id});
     message.identity = aIdentity;
-    this._mm.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
-                              message);
+    this.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
+                          message);
   },
 
   doError: function IDPAC_doError(msg) {
     log("Authentication ERROR: " + msg);
   }
 };
 
 function RPWatchContext(aOptions, aTargetMM, aPrincipal) {
@@ -118,47 +144,49 @@ function RPWatchContext(aOptions, aTarge
 
   // Maybe internal.  For hosted b2g identity shim.
   this._internal = aOptions._internal;
 
   this._mm = aTargetMM;
 }
 
 RPWatchContext.prototype = {
+  sendAsyncMessage: _sendAsyncMessage,
+
   doLogin: function RPWatchContext_onlogin(aAssertion, aMaybeInternalParams) {
     log("doLogin: " + this.id);
     let message = new IDDOMMessage({id: this.id, assertion: aAssertion});
     if (aMaybeInternalParams) {
       message._internalParams = aMaybeInternalParams;
     }
-    this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
+    this.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
   },
 
   doLogout: function RPWatchContext_onlogout() {
     log("doLogout: " + this.id);
     let message = new IDDOMMessage({id: this.id});
-    this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
+    this.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
   },
 
   doReady: function RPWatchContext_onready() {
     log("doReady: " + this.id);
     let message = new IDDOMMessage({id: this.id});
-    this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
+    this.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
   },
 
   doCancel: function RPWatchContext_oncancel() {
     log("doCancel: " + this.id);
     let message = new IDDOMMessage({id: this.id});
-    this._mm.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
+    this.sendAsyncMessage("Identity:RP:Watch:OnCancel", message);
   },
 
   doError: function RPWatchContext_onerror(aMessage) {
     log("doError: " + this.id + ": " + JSON.stringify(aMessage));
     let message = new IDDOMMessage({id: this.id, message: aMessage});
-    this._mm.sendAsyncMessage("Identity:RP:Watch:OnError", message);
+    this.sendAsyncMessage("Identity:RP:Watch:OnError", message);
   }
 };
 
 this.DOMIdentity = {
   /*
    * When relying parties (RPs) invoke the watch() method, they can request
    * to use Firefox Accounts as their auth service or BrowserID (the default).
    * For each RP, we create an RPWatchContext to store the parameters given to
@@ -202,17 +230,18 @@ this.DOMIdentity = {
    * @object message
    *         A message received from an RP.  Will include the id of the window
    *         whence the message originated.
    *
    * Returns FirefoxAccounts or IdentityService
    */
   getService: function(message) {
     if (!this._serviceContexts.has(message.id)) {
-      throw new Error("getService called before newContext for " + message.id);
+      log("ERROR: getService called before newContext for " + message.id);
+      return null;
     }
 
     let context = this._serviceContexts.get(message.id);
     if (context.wantIssuer == "firefox-accounts") {
       if (Services.prefs.getPrefType(PREF_FXA_ENABLED) === Ci.nsIPrefBranch.PREF_BOOL
           && Services.prefs.getBoolPref(PREF_FXA_ENABLED)) {
         return FirefoxAccounts;
       }
@@ -343,17 +372,19 @@ this.DOMIdentity = {
   // Private.
   _init: function DOMIdentity__init() {
     Services.ww.registerNotification(this);
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     this._subscribeListeners();
   },
 
   _subscribeListeners: function DOMIdentity__subscribeListeners() {
-    if (!ppmm) return;
+    if (!ppmm) {
+      return;
+    }
     for (let message of this.messages) {
       ppmm.addMessageListener(message, this);
     }
   },
 
   _unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
     for (let message of this.messages) {
       ppmm.removeMessageListener(message, this);
@@ -368,38 +399,53 @@ this.DOMIdentity = {
   },
 
   _unwatch: function DOMIdentity_unwatch(message, targetMM) {
     log("DOMIDentity__unwatch: " + message.id);
     // If watch failed for some reason (e.g., exception thrown because RP did
     // not have the right callbacks, we don't want unwatch to throw, because it
     // will break the process of releasing the page's resources and leak
     // memory.
-    try {
-      this.getService(message).RP.unwatch(message.id, targetMM);
-    } catch(ex) {
-      log("ERROR: can't unwatch " + message.id + ": " + ex);
+    let service = this.getService(message);
+    if (service && service.RP) {
+      service.RP.unwatch(message.id, targetMM);
+      this.deleteContextForMM(targetMM);
+      return;
     }
+    log("Can't find a service to unwatch() for " + message.id);
   },
 
   _request: function DOMIdentity__request(message) {
-    this.getService(message).RP.request(message.id, message);
+    let service = this.getService(message);
+    if (service && service.RP) {
+      service.RP.request(message.id, message);
+      return;
+    }
+    log("No context in which to call request(); Did you call watch() first?");
   },
 
   _logout: function DOMIdentity__logout(message) {
-    log("logout " + message + "\n");
-    this.getService(message).RP.logout(message.id, message.origin, message);
+    let service = this.getService(message);
+    if (service && service.RP) {
+      service.RP.logout(message.id, message.origin, message);
+      return;
+    }
+    log("No context in which to call logout(); Did you call watch() first?");
   },
 
   _childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
     if (!this.hasContextForMM(targetMM)) {
       return;
     }
 
-    this.getContextForMM(targetMM).RP.childProcessShutdown(targetMM);
+    let service = this.getContextForMM(targetMM);
+    if (service && service.RP) {
+      service.RP.childProcessShutdown(targetMM);
+    }
+
     this.deleteContextForMM(targetMM);
 
     let options = makeMessageObject({messageManager: targetMM, id: null, origin: null});
     Services.obs.notifyObservers({wrappedJSObject: options}, "identity-child-process-shutdown", null);
   },
 
   _beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
     let context = new IDPProvisioningContext(message.id, message.origin,
--- a/dom/webidl/DOMException.webidl
+++ b/dom/webidl/DOMException.webidl
@@ -51,16 +51,20 @@ interface ExceptionMembers
   // this was only ever usefully available to chrome JS.
   [ChromeOnly]
   readonly attribute StackFrame?             location;
   // An inner exception that triggered this, if available.
   readonly attribute nsISupports?            inner;
 
   // Arbitary data for the implementation.
   readonly attribute nsISupports?            data;
+
+  // Formatted exception stack
+  [Throws, Replaceable]
+  readonly attribute DOMString               stack;
 };
 
 [NoInterfaceObject]
 interface Exception {
   // A generic formatter - make it suitable to print, etc.
   stringifier;
 };
 
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -38,21 +38,25 @@ uint64_t SharedBufferManagerParent::sBuf
 class GrallocReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData, bool aAnonymize)
   {
+    if (SharedBufferManagerParent::sManagerMonitor) {
+      SharedBufferManagerParent::sManagerMonitor->Lock();
+    }
     map<base::ProcessId, SharedBufferManagerParent*>::iterator it;
     for (it = SharedBufferManagerParent::sManagers.begin(); it != SharedBufferManagerParent::sManagers.end(); it++) {
       base::ProcessId pid = it->first;
       SharedBufferManagerParent *mgr = it->second;
 
+      MutexAutoLock lock(mgr->mBuffersMutex);
       std::map<int64_t, android::sp<android::GraphicBuffer> >::iterator buf_it;
       for (buf_it = mgr->mBuffers.begin(); buf_it != mgr->mBuffers.end(); buf_it++) {
         nsresult rv;
         android::sp<android::GraphicBuffer> gb = buf_it->second;
         int bpp = android::bytesPerPixel(gb->getPixelFormat());
         int stride = gb->getStride();
         int height = gb->getHeight();
         int amount = bpp > 0
@@ -67,20 +71,26 @@ public:
             NS_LITERAL_CSTRING(
               "Special RAM that can be shared between processes and directly accessed by "
               "both the CPU and GPU. Gralloc memory is usually a relatively precious "
               "resource, with much less available than generic RAM. When it's exhausted, "
               "graphics performance can suffer. This value can be incorrect because of race "
               "conditions."),
             aData);
         if (rv != NS_OK) {
+          if (SharedBufferManagerParent::sManagerMonitor) {
+            SharedBufferManagerParent::sManagerMonitor->Unlock();
+          }
           return rv;
         }
       }
     }
+    if (SharedBufferManagerParent::sManagerMonitor) {
+      SharedBufferManagerParent::sManagerMonitor->Unlock();
+    }
     return NS_OK;
   }
 
 };
 
 NS_IMPL_ISUPPORTS(GrallocReporter, nsIMemoryReporter)
 #endif
 
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -543,37 +543,16 @@ TEST_F(AsyncPanZoomControllerTester, Con
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
   apzc->SetFrameMetrics(TestFrameMetrics());
 }
 
 TEST_F(AsyncPanZoomControllerTester, Pinch_DefaultGestures_NoTouchAction) {
   DoPinchTest(false, true);
 }
 
-TEST_F(TouchActionEnabledTester, Pinch_DefaultGestures_TouchActionNone) {
-  nsTArray<uint32_t> behaviors;
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
-  DoPinchTest(false, false, &behaviors);
-}
-
-TEST_F(TouchActionEnabledTester, Pinch_DefaultGestures_TouchActionZoom) {
-  nsTArray<uint32_t> behaviors;
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
-  DoPinchTest(false, true, &behaviors);
-}
-
-TEST_F(TouchActionEnabledTester, Pinch_DefaultGestures_TouchActionNotAllowZoom) {
-  nsTArray<uint32_t> behaviors;
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
-  behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
-  DoPinchTest(false, false, &behaviors);
-}
-
 TEST_F(AsyncPanZoomControllerTester, Pinch_UseGestureDetector_NoTouchAction) {
   DoPinchTest(true, true);
 }
 
 TEST_F(TouchActionEnabledTester, Pinch_UseGestureDetector_TouchActionNone) {
   nsTArray<uint32_t> behaviors;
   behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
   behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::NONE);
--- a/js/public/OldDebugAPI.h
+++ b/js/public/OldDebugAPI.h
@@ -32,79 +32,16 @@ class ScriptSource;
 extern JS_PUBLIC_API(unsigned)
 JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern JS_PUBLIC_API(const char *)
 JS_GetScriptFilename(JSScript *script);
 
 namespace JS {
 
-class FrameDescription
-{
-  public:
-    explicit FrameDescription(const js::FrameIter& iter);
-    FrameDescription(const FrameDescription &rhs);
-    ~FrameDescription();
-
-    unsigned lineno() {
-        if (!linenoComputed_) {
-            lineno_ = JS_PCToLineNumber(nullptr, script_, pc_);
-            linenoComputed_ = true;
-        }
-        return lineno_;
-    }
-
-    const char *filename() const {
-        return filename_;
-    }
-
-    JSFlatString *funDisplayName() const {
-        return funDisplayName_ ? JS_ASSERT_STRING_IS_FLAT(funDisplayName_) : nullptr;
-    }
-
-    // Both these locations should be traced during GC but otherwise not used;
-    // they are implementation details.
-    Heap<JSScript*> &markedLocation1() {
-        return script_;
-    }
-    Heap<JSString*> &markedLocation2() {
-        return funDisplayName_;
-    }
-
-  private:
-    void operator=(const FrameDescription &) MOZ_DELETE;
-
-    // These fields are always initialized:
-    Heap<JSString*> funDisplayName_;
-    const char *filename_;
-
-    // One of script_ xor scriptSource_ is non-null.
-    Heap<JSScript*> script_;
-    js::ScriptSource *scriptSource_;
-
-    // For script_-having frames, lineno_ is lazily computed as an optimization.
-    bool linenoComputed_;
-    unsigned lineno_;
-
-    // pc_ is non-null iff script_ is non-null. If !pc_, linenoComputed_ = true.
-    jsbytecode *pc_;
-};
-
-struct StackDescription
-{
-    unsigned nframes;
-    FrameDescription *frames;
-};
-
-extern JS_PUBLIC_API(StackDescription *)
-DescribeStack(JSContext *cx, unsigned maxFrames);
-
-extern JS_PUBLIC_API(void)
-FreeStackDescription(JSContext *cx, StackDescription *desc);
-
 extern JS_PUBLIC_API(char *)
 FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps);
 
 } // namespace JS
 
 # ifdef JS_DEBUG
 JS_FRIEND_API(void) js_DumpValue(const JS::Value &val);
 JS_FRIEND_API(void) js_DumpId(jsid id);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -50,19 +50,16 @@ class ChunkPool
     }
 
     /* Must be called with the GC lock taken. */
     inline Chunk *get(JSRuntime *rt);
 
     /* Must be called either during the GC or with the GC lock taken. */
     inline void put(Chunk *chunk);
 
-    /* Must be called with the GC lock taken. */
-    void expireAndFree(JSRuntime *rt, bool releaseAll);
-
     class Enum {
       public:
         Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {}
         bool empty() { return !*chunkp; }
         Chunk *front();
         inline void popFront();
         inline void removeAndPopFront();
       private:
@@ -353,17 +350,17 @@ class GCRuntime
     friend class ArenaLists;
     Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
-    Chunk *expireChunkPool(bool releaseAll);
+    Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
     void expireAndFreeChunkPool(bool releaseAll);
     void freeChunkList(Chunk *chunkListHead);
     void prepareToFreeChunk(ChunkInfo &info);
     void releaseChunk(Chunk *chunk);
 
     inline bool wantBackgroundAllocation() const;
 
     bool initZeal();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4728,17 +4728,17 @@ JS::CompileFunction(JSContext *cx, Handl
                     const char *bytes, size_t length, MutableHandleFunction fun)
 {
     mozilla::ScopedFreePtr<jschar> chars;
     if (options.utf8)
         chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
     else
         chars = InflateString(cx, bytes, &length);
     if (!chars)
-        return nullptr;
+        return false;
 
     return CompileFunction(cx, obj, options, name, nargs, argnames, chars, length, fun);
 }
 
 JS_PUBLIC_API(bool)
 JS_CompileUCFunction(JSContext *cx, JS::HandleObject obj, const char *name,
                      unsigned nargs, const char *const *argnames,
                      const jschar *chars, size_t length,
@@ -5717,33 +5717,53 @@ JS_Stringify(JSContext *cx, MutableHandl
     if (!js_Stringify(cx, vp, replacer, space, sb))
         return false;
     if (sb.empty() && !sb.append(cx->names().null))
         return false;
     return callback(sb.rawTwoByteBegin(), sb.length(), data);
 }
 
 JS_PUBLIC_API(bool)
-JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, JS::MutableHandleValue vp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-
-    RootedValue reviver(cx, NullValue());
-    return ParseJSONWithReviver(cx, mozilla::Range<const jschar>(chars, len), reviver, vp);
+JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, MutableHandleValue vp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    return ParseJSONWithReviver(cx, mozilla::Range<const jschar>(chars, len), NullHandleValue, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_ParseJSON(JSContext *cx, HandleString str, MutableHandleValue vp)
+{
+    return JS_ParseJSONWithReviver(cx, str, NullHandleValue, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, HandleValue reviver, MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return ParseJSONWithReviver(cx, mozilla::Range<const jschar>(chars, len), reviver, vp);
 }
 
+JS_PUBLIC_API(bool)
+JS_ParseJSONWithReviver(JSContext *cx, HandleString str, HandleValue reviver, MutableHandleValue vp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, str);
+
+    AutoStableStringChars stableChars(cx);
+    if (!stableChars.init(cx, str))
+        return false;
+
+    return stableChars.isLatin1()
+           ? ParseJSONWithReviver(cx, stableChars.latin1Range(), reviver, vp)
+           : ParseJSONWithReviver(cx, stableChars.twoByteRange(), reviver, vp);
+}
+
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_ReportError(JSContext *cx, const char *format, ...)
 {
     va_list ap;
 
     AssertHeapIsIdle(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4479,19 +4479,26 @@ JS_Stringify(JSContext *cx, JS::MutableH
 
 /*
  * JSON.parse as specified by ES5.
  */
 JS_PUBLIC_API(bool)
 JS_ParseJSON(JSContext *cx, const jschar *chars, uint32_t len, JS::MutableHandleValue vp);
 
 JS_PUBLIC_API(bool)
+JS_ParseJSON(JSContext *cx, JS::HandleString str, JS::MutableHandleValue vp);
+
+JS_PUBLIC_API(bool)
 JS_ParseJSONWithReviver(JSContext *cx, const jschar *chars, uint32_t len, JS::HandleValue reviver,
                         JS::MutableHandleValue vp);
 
+JS_PUBLIC_API(bool)
+JS_ParseJSONWithReviver(JSContext *cx, JS::HandleString str, JS::HandleValue reviver,
+                        JS::MutableHandleValue vp);
+
 /************************************************************************/
 
 /*
  * The default locale for the ECMAScript Internationalization API
  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
  * Note that the Internationalization API encourages clients to
  * specify their own locales.
  * The locale string remains owned by the caller.
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -130,16 +130,17 @@ class CompartmentChecker
 
     void check(JSScript *script) {
         if (script)
             check(script->compartment());
     }
 
     void check(InterpreterFrame *fp);
     void check(AbstractFramePtr frame);
+    void check(SavedStacks *stacks);
 };
 #endif /* JS_CRASH_DIAGNOSTICS */
 
 /*
  * Don't perform these checks when called from a finalizer. The checking
  * depends on other objects not having been swept yet.
  */
 #define START_ASSERT_SAME_COMPARTMENT()                                       \
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -233,20 +233,26 @@ using mozilla::Swap;
 using JS::AutoGCRooter;
 
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
 /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
 static const int IGC_MARK_SLICE_MULTIPLIER = 2;
 
+#ifdef JSGC_GENERATIONAL
+static const unsigned MIN_EMPTY_CHUNK_COUNT = 1;
+#else
+static const unsigned MIN_EMPTY_CHUNK_COUNT = 0;
+#endif
+
 #if defined(ANDROID) || defined(MOZ_B2G)
-static const int MAX_EMPTY_CHUNK_COUNT = 2;
+static const unsigned MAX_EMPTY_CHUNK_COUNT = 2;
 #else
-static const int MAX_EMPTY_CHUNK_COUNT = 30;
+static const unsigned MAX_EMPTY_CHUNK_COUNT = 30;
 #endif
 
 const AllocKind gc::slotsToThingKind[] = {
     /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
     /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
     /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
     /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
     /* 16 */ FINALIZE_OBJECT16
@@ -676,44 +682,46 @@ ChunkPool::Enum::removeAndPopFront()
 {
     JS_ASSERT(!empty());
     *chunkp = front()->info.next;
     --pool.emptyCount;
 }
 
 /* Must be called either during the GC or with the GC lock taken. */
 Chunk *
-GCRuntime::expireChunkPool(bool releaseAll)
+GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
     Chunk *freeList = nullptr;
-    int freeChunkCount = 0;
+    unsigned freeChunkCount = 0;
     for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
         Chunk *chunk = e.front();
         JS_ASSERT(chunk->unused());
         JS_ASSERT(!chunkSet.has(chunk));
-        JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE);
-        if (releaseAll || chunk->info.age == MAX_EMPTY_CHUNK_AGE ||
-            freeChunkCount++ > MAX_EMPTY_CHUNK_COUNT)
+        if (releaseAll || freeChunkCount >= MAX_EMPTY_CHUNK_COUNT ||
+            (freeChunkCount >= MIN_EMPTY_CHUNK_COUNT &&
+             (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             e.removeAndPopFront();
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
+            ++freeChunkCount;
             ++chunk->info.age;
             e.popFront();
         }
     }
+    JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= MIN_EMPTY_CHUNK_COUNT);
     JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
     return freeList;
 }
 
 void
 GCRuntime::freeChunkList(Chunk *chunkListHead)
 {
     while (Chunk *chunk = chunkListHead) {
@@ -721,17 +729,17 @@ GCRuntime::freeChunkList(Chunk *chunkLis
         chunkListHead = chunk->info.next;
         FreeChunk(rt, chunk);
     }
 }
 
 void
 GCRuntime::expireAndFreeChunkPool(bool releaseAll)
 {
-    freeChunkList(expireChunkPool(releaseAll));
+    freeChunkList(expireChunkPool(true, releaseAll));
 }
 
 /* static */ Chunk *
 Chunk::allocate(JSRuntime *rt)
 {
     Chunk *chunk = AllocChunk(rt);
     if (!chunk)
         return nullptr;
@@ -1019,17 +1027,17 @@ inline bool
 GCRuntime::wantBackgroundAllocation() const
 {
     /*
      * To minimize memory waste we do not want to run the background chunk
      * allocation if we have empty chunks or when the runtime needs just few
      * of them.
      */
     return helperState.canBackgroundAllocate() &&
-           chunkPool.getEmptyCount() == 0 &&
+           chunkPool.getEmptyCount() < MIN_EMPTY_CHUNK_COUNT &&
            chunkSet.count() >= 4;
 }
 
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -2563,17 +2571,17 @@ GCRuntime::decommitArenas()
 /* Must be called with the GC lock taken. */
 void
 GCRuntime::expireChunksAndArenas(bool shouldShrink)
 {
 #ifdef JSGC_FJGENERATIONAL
     rt->threadPool.pruneChunkCache();
 #endif
 
-    if (Chunk *toFree = expireChunkPool(shouldShrink)) {
+    if (Chunk *toFree = expireChunkPool(shouldShrink, false)) {
         AutoUnlockGC unlock(rt);
         freeChunkList(toFree);
     }
 
     if (shouldShrink)
         decommitArenas();
 }
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4178,17 +4178,17 @@ static const JSFunctionSpec DebuggerSour
 
 
 /*** Debugger.Frame ******************************************************************************/
 
 static void
 UpdateFrameIterPc(FrameIter &iter)
 {
     if (iter.abstractFramePtr().isRematerializedFrame()) {
-#ifdef DEBUG
+#if defined(DEBUG) && defined(JS_ION)
         // Rematerialized frames don't need their pc updated. The reason we
         // need to update pc is because we might get the same Debugger.Frame
         // object for multiple re-entries into debugger code from debuggee
         // code. This reentrancy is not possible with rematerialized frames,
         // because when returning to debuggee code, we would have bailed out
         // to baseline.
         //
         // We walk the stack to assert that it doesn't need updating.
--- a/js/src/vm/OldDebugAPI.cpp
+++ b/js/src/vm/OldDebugAPI.cpp
@@ -890,95 +890,16 @@ js_CallContextDebugHandler(JSContext *cx
         return false;
       case JSTRAP_RETURN:
       case JSTRAP_CONTINUE:
       default:
         return true;
     }
 }
 
-/*
- * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid
- * constructing a FrameDescription on the stack just to append it to a vector.
- * FrameDescription contains Heap<T> fields that should not live on the stack.
- */
-JS::FrameDescription::FrameDescription(const FrameIter& iter)
-  : scriptSource_(nullptr),
-    linenoComputed_(false),
-    pc_(nullptr)
-{
-    if (iter.isNonEvalFunctionFrame())
-        funDisplayName_ = iter.functionDisplayAtom();
-
-    if (iter.hasScript()) {
-        script_ = iter.script();
-        pc_ = iter.pc();
-        filename_ = script_->filename();
-    } else {
-        scriptSource_ = iter.scriptSource();
-        scriptSource_->incref();
-        filename_ = scriptSource_->filename();
-        lineno_ = iter.computeLine();
-        linenoComputed_ = true;
-    }
-}
-
-JS::FrameDescription::FrameDescription(const FrameDescription &rhs)
-  : funDisplayName_(rhs.funDisplayName_),
-    filename_(rhs.filename_),
-    script_(rhs.script_),
-    scriptSource_(rhs.scriptSource_),
-    linenoComputed_(rhs.linenoComputed_),
-    lineno_(rhs.lineno_),
-    pc_(rhs.pc_)
-{
-    if (scriptSource_)
-        scriptSource_->incref();
-}
-
-
-JS::FrameDescription::~FrameDescription()
-{
-    if (scriptSource_)
-        scriptSource_->decref();
-}
-
-JS_PUBLIC_API(JS::StackDescription *)
-JS::DescribeStack(JSContext *cx, unsigned maxFrames)
-{
-    Vector<FrameDescription> frames(cx);
-
-    NonBuiltinFrameIter i(cx, FrameIter::ALL_CONTEXTS,
-                          FrameIter::GO_THROUGH_SAVED,
-                          cx->compartment()->principals);
-    for ( ; !i.done(); ++i) {
-        if (!frames.append(i))
-            return nullptr;
-        if (frames.length() == maxFrames)
-            break;
-    }
-
-    JS::StackDescription *desc = js_new<JS::StackDescription>();
-    if (!desc)
-        return nullptr;
-
-    desc->nframes = frames.length();
-    desc->frames = frames.extractRawBuffer();
-    return desc;
-}
-
-JS_PUBLIC_API(void)
-JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc)
-{
-    for (size_t i = 0; i < desc->nframes; ++i)
-        desc->frames[i].~FrameDescription();
-    js_free(desc->frames);
-    js_delete(desc);
-}
-
 namespace {
 
 class AutoPropertyDescArray
 {
     JSContext *cx_;
     JSPropertyDescArray descArray_;
 
   public:
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -11,16 +11,17 @@
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 
 #include "gc/Marking.h"
 #include "vm/GlobalObject.h"
 #include "vm/StringBuffer.h"
 
+#include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using mozilla::AddToHash;
 using mozilla::HashString;
 
 namespace js {
 
 struct SavedFrame::Lookup {
@@ -394,17 +395,17 @@ SavedStacks::init()
 
     return frames.init();
 }
 
 bool
 SavedStacks::saveCurrentStack(JSContext *cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
 {
     JS_ASSERT(initialized());
-    JS_ASSERT(&cx->compartment()->savedStacks() == this);
+    assertSameCompartment(cx, this);
 
     FrameIter iter(cx, FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED);
     return insertFrames(cx, iter, frame, maxFrameCount);
 }
 
 void
 SavedStacks::sweep(JSRuntime *rt)
 {
@@ -499,18 +500,21 @@ SavedStacks::insertFrames(JSContext *cx,
     // potentially memoized location result and copy it into |location|. When we
     // do not have a |JSScript| for this frame (asm.js frames), we take a slow
     // path that doesn't employ memoization, and update |location|'s slots
     // directly.
     AutoLocationValueRooter location(cx);
     if (iter.hasScript()) {
         JSScript *script = iter.script();
         jsbytecode *pc = iter.pc();
-        if (!getLocation(cx, script, pc, &location))
-            return false;
+        {
+            AutoCompartment ac(cx, iter.compartment());
+            if (!cx->compartment()->savedStacks().getLocation(cx, script, pc, &location))
+                return false;
+        }
     } else {
         const char *filename = iter.scriptFilename();
         if (!filename)
             filename = "";
         location.get().source = Atomize(cx, filename, strlen(filename));
         if (!location.get().source)
             return false;
         uint32_t column;
@@ -590,23 +594,23 @@ SavedStacks::getOrCreateSavedFrameProtot
 
 SavedFrame *
 SavedStacks::createFrameFromLookup(JSContext *cx, const SavedFrame::Lookup &lookup)
 {
     RootedObject proto(cx, getOrCreateSavedFramePrototype(cx));
     if (!proto)
         return nullptr;
 
-    JS_ASSERT(proto->compartment() == cx->compartment());
+    assertSameCompartment(cx, proto);
 
     RootedObject global(cx, cx->compartment()->maybeGlobal());
     if (!global)
         return nullptr;
 
-    JS_ASSERT(global->compartment() == cx->compartment());
+    assertSameCompartment(cx, global);
 
     RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto, global));
     if (!frameObj)
         return nullptr;
 
     SavedFrame &f = frameObj->as<SavedFrame>();
     f.initFromLookup(lookup);
 
@@ -630,16 +634,22 @@ SavedStacks::sweepPCLocationMap()
         }
     }
 }
 
 bool
 SavedStacks::getLocation(JSContext *cx, JSScript *script, jsbytecode *pc,
                          MutableHandleLocationValue locationp)
 {
+    // We should only ever be caching location values for scripts in this
+    // compartment. Otherwise, we would get dead cross-compartment scripts in
+    // the cache because our compartment's sweep method isn't called when their
+    // compartment gets collected.
+    assertSameCompartment(cx, this, script);
+
     PCKey key(script, pc);
     PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key);
 
     if (!p) {
         const char *filename = script->filename() ? script->filename() : "";
         RootedAtom source(cx, Atomize(cx, filename, strlen(filename)));
         if (!source)
             return false;
@@ -661,9 +671,21 @@ SavedStacksMetadataCallback(JSContext *c
 {
     RootedSavedFrame frame(cx);
     if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
         return false;
     *pmetadata = frame;
     return true;
 }
 
+#ifdef JS_CRASH_DIAGNOSTICS
+void
+CompartmentChecker::check(SavedStacks *stacks)
+{
+    if (&compartment->savedStacks() != stacks) {
+        printf("*** Compartment SavedStacks mismatch: %p vs. %p\n",
+               (void *) &compartment->savedStacks(), stacks);
+        MOZ_CRASH();
+    }
+}
+#endif /* JS_CRASH_DIAGNOSTICS */
+
 } /* namespace js */
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -108,18 +108,18 @@ class SavedStacks {
     void     sweep(JSRuntime *rt);
     void     trace(JSTracer *trc);
     uint32_t count();
     void     clear();
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
   private:
-    SavedFrame::Set          frames;
-    JSObject                 *savedFrameProto;
+    SavedFrame::Set frames;
+    JSObject        *savedFrameProto;
 
     bool       insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFrame frame,
                             unsigned maxFrameCount = 0);
     SavedFrame *getOrCreateSavedFrame(JSContext *cx, const SavedFrame::Lookup &lookup);
     // |SavedFrame.prototype| is created lazily and held weakly. It should only
     // be accessed through this method.
     JSObject   *getOrCreateSavedFramePrototype(JSContext *cx);
     SavedFrame *createFrameFromLookup(JSContext *cx, const SavedFrame::Lookup &lookup);
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -224,17 +224,17 @@ CloningFunctionForwarder(JSContext *cx, 
         cloneOptions.wrapReflectors = true;
         for (unsigned i = 0; i < args.length(); i++) {
             RootedObject argObj(cx, args[i].isObject() ? &args[i].toObject() : nullptr);
             if (options.allowCallbacks && argObj && JS_ObjectIsCallable(cx, argObj)) {
                 FunctionForwarderOptions innerOptions(cx);
                 if (!JS_WrapObject(cx, &argObj))
                     return false;
                 if (!xpc::NewFunctionForwarder(cx, JSID_VOIDHANDLE, argObj, innerOptions, args[i]))
-                    return nullptr;
+                    return false;
             } else if (!StackScopedClone(cx, cloneOptions, args[i])) {
                 return false;
             }
         }
 
         // JS API does not support any JSObject to JSFunction conversion,
         // so let's use JS_CallFunctionValue instead.
         RootedValue functionVal(cx, ObjectValue(*origFunObj));
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -49,24 +49,26 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     // Test constructors that can be instantiated with zero arguments.
     for (var c of simpleConstructors) {
       ok(iwin[c], "Constructors appear: " + c);
       is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
          "we end up with the appropriate constructor: " + c);
       is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c]).constructor), iwin[c],
          "constructor property is set up right: " + c);
-      is(Object.getPrototypeOf(new iwin[c]), iwin[c].prototype,
+      let expectedProto = /Opaque/.test(new iwin[c]) ? iwin['Object'].prototype
+                                                     : iwin[c].prototype;
+      is(Object.getPrototypeOf(new iwin[c]), expectedProto,
          "prototype is correct: " + c);
       is(global(new iwin[c]), iwin, "Got the right global: " + c);
     }
 
     // Test Object in more detail.
     var num = new iwin.Object(4);
-    is(num.valueOf(), 4, "primitive object construction works");
+    is(Cu.waiveXrays(num).valueOf(), 4, "primitive object construction works");
     is(global(num), iwin, "correct global for num");
     var obj = new iwin.Object();
     obj.foo = 2;
     var withProto = Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(obj));
     is(global(withProto), iwin, "correct global for withProto");
     is(Cu.waiveXrays(withProto).foo, 2, "Inherits properly");
 
     // Test Function.
@@ -100,36 +102,29 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(Cu.isXrayWrapper(foopyFunction), "Should be Xrays");
     is(foopyFunction.name, "namedFoopyFunction", ".name works over Xrays");
     is(foopyFunction.length, 3, ".length works over Xrays");
     ok(Object.getOwnPropertyNames(foopyFunction).indexOf('length') >= 0, "Should list length");
     ok(Object.getOwnPropertyNames(foopyFunction).indexOf('name') >= 0, "Should list name");
     ok(Object.getOwnPropertyNames(foopyFunction).indexOf('prototype') == -1, "Should not list prototype");
     ok(Object.getOwnPropertyNames(iwin.Array).indexOf('prototype') >= 0, "Should list prototype for standard constructor");
 
-    // Test interface objects that don't actually construct things.
-    is(iwin.Math.tan(4.5), Math.tan(4.5), "Math.tan works");
-    is(iwin.Math.E, Math.E, "Math.E works");
-    var json = JSON.stringify({a: 2, b: 'hi', c: {d: 'there'}});
-    is(global(iwin.JSON.parse(json)), iwin, "JSON rehydrated into the right context");
-    is(iwin.JSON.stringify(iwin.JSON.parse(json)), json, "JSON composition identity holds");
-
     // Test proxies.
     var targetObject = new iwin.Object();
     targetObject.foo = 9;
     var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
     is(global(forwardingProxy), iwin, "proxy global correct");
-    is(forwardingProxy.foo, 9, "forwards correctly");
+    is(Cu.waiveXrays(forwardingProxy).foo, 9, "forwards correctly");
     // NB: COW-implemented proxy handlers are super dangerous, and we should not
     // encourage them.
     var handler = {get: function(target, name) { return name * 2; }, __exposedProps__: {get: 'r'}};
     var doublingProxy = new iwin.Proxy(targetObject, handler);
     is(global(doublingProxy), iwin, "doubling proxy global correct");
-    is(doublingProxy[3], 6, "Doubles correctly");
-    is(doublingProxy[20], 40, "Doubles correctly");
+    is(Cu.waiveXrays(doublingProxy)[3], 6, "Doubles correctly");
+    is(Cu.waiveXrays(doublingProxy)[20], 40, "Doubles correctly");
 
     // Test eval.
     var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
     is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
     is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
     is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
 
     testDate();
@@ -245,18 +240,26 @@ https://bugzilla.mozilla.org/show_bug.cg
       ok(method instanceof Function, "instanceof works on methods from Xrays");
       is(lookupCallable(xrayProto), method, "Holder caching works properly");
       is(lookupCallable(xray), method, "Proto props resolve on the instance");
       let local = lookupCallable(localProto);
       is(method.length, local.length, "Function.length identical");
       if (method.length == 0) {
         is(method.call(xray) + "", local.call(xray) + "",
            "Xray and local method results stringify identically");
-         is(method.call(xray) + "", lookupCallable(xray.wrappedJSObject).call(xray.wrappedJSObject) + "",
-            "Xray and waived method results stringify identically");
+
+        // If invoking this method returns something non-Xrayable, the
+        // stringification is going to return [object Opaque]. This happens for
+        // xrayedTypedArray.buffer, for instance, since we don't have Xrays
+        // To ArrayBuffers. Just check for that case.
+        if (!/Opaque/.test(method.call(xray))) {
+          is(method.call(xray) + "",
+             lookupCallable(xray.wrappedJSObject).call(xray.wrappedJSObject) + "",
+             "Xray and waived method results stringify identically");
+        }
       }
     }
     is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
        protoProps.toSource(), "getOwnPropertyNames works");
 
     is(xrayProto.constructor, iwin[classname], "constructor property works");
 
     xrayProto.expando = 42;
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug856067.js
@@ -0,0 +1,10 @@
+const Cu = Components.utils;
+
+function run_test() {
+  var sb = new Cu.Sandbox('http://www.example.com');
+  let w = Cu.evalInSandbox('var w = WeakMap(); w.__proto__ = new Set(); w.foopy = 12; w', sb);
+  do_check_eq(Object.getPrototypeOf(w), sb.Object.prototype);
+  do_check_eq(Object.getOwnPropertyNames(w).length, 0);
+  do_check_eq(w.wrappedJSObject.foopy, 12);
+  do_check_eq(w.foopy, undefined);
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -30,16 +30,17 @@ support-files =
 [test_bug809652.js]
 [test_bug809674.js]
 [test_bug813901.js]
 [test_bug845201.js]
 [test_bug845862.js]
 [test_bug849730.js]
 [test_bug851895.js]
 [test_bug854558.js]
+[test_bug856067.js]
 [test_bug868675.js]
 [test_bug867486.js]
 [test_bug872772.js]
 [test_bug885800.js]
 [test_bug961054.js]
 [test_bug976151.js]
 [test_bug1001094.js]
 [test_bug1021312.js]
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -365,18 +365,20 @@ SelectWrapper(bool securityWrapper, bool
 
     // Ok, we're using Xray. If this isn't a security wrapper, use the permissive
     // version and skip the filter.
     if (!securityWrapper) {
         if (xrayType == XrayForWrappedNative)
             return &PermissiveXrayXPCWN::singleton;
         else if (xrayType == XrayForDOMObject)
             return &PermissiveXrayDOM::singleton;
-        MOZ_ASSERT(xrayType == XrayForJSObject);
-        return &PermissiveXrayJS::singleton;
+        else if (xrayType == XrayForJSObject)
+            return &PermissiveXrayJS::singleton;
+        MOZ_ASSERT(xrayType == XrayForOpaqueObject);
+        return &PermissiveXrayOpaque::singleton;
     }
 
     // This is a security wrapper. Use the security versions and filter.
     if (xrayType == XrayForWrappedNative)
         return &FilteringWrapper<SecurityXrayXPCWN,
                                  CrossOriginAccessiblePropertiesOnly>::singleton;
     else if (xrayType == XrayForDOMObject)
         return &FilteringWrapper<SecurityXrayDOM,
@@ -384,17 +386,17 @@ SelectWrapper(bool securityWrapper, bool
     // There's never any reason to expose pure JS objects to non-subsuming actors.
     // Just use an opaque wrapper in this case.
     //
     // In general, we don't want opaque function wrappers to be callable.
     // But in the case of XBL, we rely on content being able to invoke
     // functions exposed from the XBL scope. We could remove this exception,
     // if needed, by using ExportFunction to generate the content-side
     // representations of XBL methods.
-    MOZ_ASSERT(xrayType == XrayForJSObject);
+    MOZ_ASSERT(xrayType == XrayForJSObject || xrayType == XrayForOpaqueObject);
     if (originIsXBLScope)
         return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
     return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton;
 }
 
 JSObject *
 WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
                        HandleObject parent, unsigned flags)
@@ -438,30 +440,16 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     // We make an exception for Object instances, because we still rely on COWs
     // for those in a lot of places in the tree.
     else if (originIsChrome && !targetIsChrome &&
              (xrayType == NotXray || ForceCOWBehavior(obj)))
     {
         wrapper = &ChromeObjectWrapper::singleton;
     }
 
-    // Normally, a non-xrayable non-waived content object that finds itself in
-    // a privileged scope is wrapped with a CrossCompartmentWrapper, even though
-    // the lack of a waiver _really_ should give it an opaque wrapper. This is
-    // a bit too entrenched to change for content-chrome, but we can at least fix
-    // it for XBL scopes.
-    //
-    // See bug 843829.
-    else if (targetSubsumesOrigin && !originSubsumesTarget &&
-             !waiveXrayFlag && xrayType == NotXray &&
-             IsContentXBLScope(target))
-    {
-        wrapper = &PermissiveXrayOpaque::singleton;
-    }
-
     //
     // Now, handle the regular cases.
     //
     // These are wrappers we can compute using a rule-based approach. In order
     // to do so, we need to compute some parameters.
     //
     else {
 
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -64,16 +64,18 @@ IsErrorObjectKey(JSProtoKey key)
 }
 
 inline bool
 IsTypedArrayKey(JSProtoKey key)
 {
     return key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray;
 }
 
+bool SilentFailure(JSContext *cx, JS::HandleId id, const char *reason);
+
 // Whitelist for the standard ES classes we can Xray to.
 static bool
 IsJSXraySupported(JSProtoKey key)
 {
     if (IsTypedArrayKey(key))
         return true;
     if (IsErrorObjectKey(key))
         return true;
@@ -98,17 +100,24 @@ GetXrayType(JSObject *obj)
     const js::Class* clasp = js::GetObjectClass(obj);
     if (IS_WN_CLASS(clasp) || clasp->ext.innerObject)
         return XrayForWrappedNative;
 
     JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj);
     if (IsJSXraySupported(standardProto))
         return XrayForJSObject;
 
-    return NotXray;
+    // Modulo a few exceptions, everything else counts as an XrayWrapper to an
+    // opaque object, which means that more-privileged code sees nothing from
+    // the underlying object. This is very important for security. In some cases
+    // though, we need to make an exception for compatibility.
+    if (IsSandbox(obj))
+        return NotXray;
+
+    return XrayForOpaqueObject;
 }
 
 JSObject *
 XrayAwareCalleeGlobal(JSObject *fun)
 {
   MOZ_ASSERT(js::IsFunctionObject(fun));
   JSObject *scope = js::GetObjectParent(fun);
   if (IsXrayWrapper(scope))
@@ -481,16 +490,27 @@ public:
 
     virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
                                        HandleObject holder, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE
     {
         MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
     }
 
+    virtual bool resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper, HandleObject wrapper,
+                                    HandleObject holder, HandleId id,
+                                    MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE
+    {
+        bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
+        if (!ok || desc.object())
+            return ok;
+
+        return SilentFailure(cx, id, "Object is not safely Xrayable");
+    }
+
     bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                         MutableHandle<JSPropertyDescriptor> desc,
                         Handle<JSPropertyDescriptor> existingDesc, bool *defined)
     {
         *defined = false;
         return true;
     }
 
@@ -548,27 +568,27 @@ public:
     {
         RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper));
         return JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global);
     }
 
     static OpaqueXrayTraits singleton;
 };
 
-inline bool
+bool
 SilentFailure(JSContext *cx, HandleId id, const char *reason)
 {
 #ifdef DEBUG
     nsAutoJSString name;
     if (!name.init(cx, id))
         return false;
     AutoFilename filename;
     unsigned line = 0;
     DescribeScriptedCaller(cx, &filename, &line);
-    NS_WARNING(nsPrintfCString("Denied access to property |%s| on Xrayed Object: %s (@%s:%u)",
+    NS_WARNING(nsPrintfCString("Silently denied access to property |%s|: %s (@%s:%u)",
                                NS_LossyConvertUTF16toASCII(name).get(), reason,
                                filename.get(), line).get());
 #endif
     return true;
 }
 
 bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext *cx,
                                                   HandleObject target,
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1037903.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="-moz-column-width: calc(15px);">
+<body>
+<video></video><audio style="box-decoration-break: clone; display: block; direction: rtl;"></audio>
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -530,8 +530,9 @@ load 973701-2.xhtml
 load 986899.html
 load 1001233.html
 load 1001258-1.html
 pref(layout.css.grid.enabled,true) load 1015562.html
 asserts(1-2) load 1015563-1.html
 asserts(1-2) load 1015563-2.html
 load outline-on-frameset.xhtml
 pref(font.size.inflation.minTwips,200) load 1032450.html
+load 1037903.html
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -171,17 +171,19 @@ static nscoord
 GetBEndMarginClone(nsIFrame* aFrame,
                    nsRenderingContext* aRenderingContext,
                    const LogicalRect& aContentArea,
                    WritingMode aWritingMode)
 {
   if (aFrame->StyleBorder()->mBoxDecorationBreak ==
         NS_STYLE_BOX_DECORATION_BREAK_CLONE) {
     nsCSSOffsetState os(aFrame, aRenderingContext, aContentArea.Width(aWritingMode));
-    return os.ComputedLogicalMargin().BEnd(aWritingMode);
+    return os.ComputedLogicalMargin().
+                ConvertTo(aWritingMode,
+                          aFrame->GetWritingMode()).BEnd(aWritingMode);
   }
   return 0;
 }
 
 // Compute the amount of available space for reflowing a block frame
 // at the current Y coordinate. This method assumes that
 // GetAvailableSpace has already been called.
 void
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -38,16 +38,17 @@ XPIDL_SOURCES += [
     'nsIDashboard.idl',
     'nsIDashboardEventNotifier.idl',
     'nsIDivertableChannel.idl',
     'nsIDownloader.idl',
     'nsIEncodedChannel.idl',
     'nsIExternalProtocolHandler.idl',
     'nsIFileStreams.idl',
     'nsIFileURL.idl',
+    'nsIForcePendingChannel.idl',
     'nsIIncrementalDownload.idl',
     'nsIInputStreamChannel.idl',
     'nsIInputStreamPump.idl',
     'nsIIOService.idl',
     'nsIIOService2.idl',
     'nsILoadContextInfo.idl',
     'nsILoadGroup.idl',
     'nsILoadGroupChild.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIForcePendingChannel.idl
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsIForcePending interface exposes a function that enables overwriting of the normal 
+ * behavior for the channel's IsPending(), forcing 'true' to be returned.
+ */
+
+[scriptable, uuid(225ab092-1554-423a-9492-606f6db3b4fb)]
+interface nsIForcePendingChannel : nsISupports
+{
+
+/**
+     * forcePending(true) overrides the normal behavior for the 
+     * channel's IsPending(), forcing 'true' to be returned. A call to
+     * forcePending(false) reverts IsPending() back to normal behavior.
+     */
+    void forcePending(in boolean aForcePending);
+};
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -4,16 +4,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/net/FTPChannelParent.h"
 #include "nsFTPChannel.h"
 #include "nsNetUtil.h"
 #include "nsFtpProtocolHandler.h"
+#include "nsIEncodedChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIForcePendingChannel.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/unused.h"
 #include "SerializedLoadContext.h"
 
 using namespace mozilla::ipc;
 
 #undef LOG
@@ -52,17 +55,18 @@ FTPChannelParent::ActorDestroy(ActorDest
 //-----------------------------------------------------------------------------
 // FTPChannelParent::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(FTPChannelParent,
                   nsIStreamListener,
                   nsIParentChannel,
                   nsIInterfaceRequestor,
-                  nsIRequestObserver)
+                  nsIRequestObserver,
+                  nsIChannelEventSink)
 
 //-----------------------------------------------------------------------------
 // FTPChannelParent::PFTPChannelParent
 //-----------------------------------------------------------------------------
 
 //-----------------------------------------------------------------------------
 // FTPChannelParent methods
 //-----------------------------------------------------------------------------
@@ -109,57 +113,60 @@ FTPChannelParent::DoAsyncOpen(const URIP
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
   nsCOMPtr<nsIChannel> chan;
   rv = NS_NewChannel(getter_AddRefs(chan), uri, ios);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
-  mChannel = static_cast<nsFtpChannel*>(chan.get());
+  mChannel = chan;
+
+  // later on mChannel may become an HTTP channel (we'll be redirected to one
+  // if we're using a proxy), but for now this is safe
+  nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get());
 
   if (mPBOverride != kPBOverride_Unset) {
-    mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
+    ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
   }
-
-  rv = mChannel->SetNotificationCallbacks(this);
+  rv = ftpChan->SetNotificationCallbacks(this);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
   nsTArray<mozilla::ipc::FileDescriptor> fds;
   nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
   if (upload) {
     // contentType and contentLength are ignored
-    rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
+    rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0);
     if (NS_FAILED(rv))
       return SendFailedAsyncOpen(rv);
   }
 
-  rv = mChannel->ResumeAt(aStartPos, aEntityID);
+  rv = ftpChan->ResumeAt(aStartPos, aEntityID);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
 
-  rv = mChannel->AsyncOpen(this, nullptr);
+  rv = ftpChan->AsyncOpen(this, nullptr);
   if (NS_FAILED(rv))
     return SendFailedAsyncOpen(rv);
   
   return true;
 }
 
 bool
 FTPChannelParent::ConnectChannel(const uint32_t& channelId)
 {
   nsresult rv;
 
   LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
   if (NS_SUCCEEDED(rv))
-    mChannel = static_cast<nsFtpChannel*>(channel.get());
+    mChannel = channel;
 
   LOG(("  found channel %p, rv=%08x", mChannel.get(), rv));
 
   return true;
 }
 
 bool
 FTPChannelParent::RecvCancel(const nsresult& status)
@@ -235,17 +242,20 @@ FTPChannelParent::RecvDivertOnStopReques
     return false;
   }
 
   // Honor the channel's status even if the underlying transaction completed.
   nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
 
   // Reset fake pending status in case OnStopRequest has already been called.
   if (mChannel) {
-    mChannel->ForcePending(false);
+    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+    if (forcePendingIChan) {
+      forcePendingIChan->ForcePending(false);
+    }
   }
 
   OnStopRequest(mChannel, nullptr, status);
   return true;
 }
 
 bool
 FTPChannelParent::RecvDivertComplete()
@@ -292,24 +302,24 @@ FTPChannelParent::OnStartRequest(nsIRequ
 
   nsCString entityID;
   nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
   MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
   if (resChan) {
     resChan->GetEntityID(entityID);
   }
 
+  PRTime lastModified = 0;
   nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
-  PRTime lastModified = 0;
   if (ftpChan) {
     ftpChan->GetLastModifiedTime(&lastModified);
-  } else {
-    // Temporary hack: if we were redirected to use an HTTP channel (ie FTP is
-    // using an HTTP proxy), cancel, as we don't support those redirects yet.
-    aRequest->Cancel(NS_ERROR_NOT_IMPLEMENTED);
+  }
+  nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest);
+  if (httpChan) {
+    httpChan->GetLastModifiedTime(&lastModified);
   }
 
   URIParams uriparam;
   nsCOMPtr<nsIURI> uri;
   chan->GetURI(getter_AddRefs(uri));
   SerializeURI(uri, uriparam);
 
   if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
@@ -494,17 +504,20 @@ FTPChannelParent::StartDiversion()
   if (NS_WARN_IF(!mDivertingFromChild)) {
     MOZ_ASSERT(mDivertingFromChild,
                "Cannot StartDiversion if diverting is not set!");
     return;
   }
 
   // Fake pending status in case OnStopRequest has already been called.
   if (mChannel) {
-    mChannel->ForcePending(true);
+    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+    if (forcePendingIChan) {
+      forcePendingIChan->ForcePending(true);
+    }
   }
 
   // Call OnStartRequest for the "DivertTo" listener.
   nsresult rv = OnStartRequest(mChannel, nullptr);
   if (NS_FAILED(rv)) {
     if (mChannel) {
       mChannel->Cancel(rv);
     }
@@ -562,43 +575,74 @@ FTPChannelParent::NotifyDiversionFailed(
                                         bool aSkipResume)
 {
   MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
   MOZ_RELEASE_ASSERT(mDivertingFromChild);
   MOZ_RELEASE_ASSERT(mDivertToListener);
   MOZ_RELEASE_ASSERT(mChannel);
 
   mChannel->Cancel(aErrorCode);
-
-  mChannel->ForcePending(false);
+  nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+  if (forcePendingIChan) {
+    forcePendingIChan->ForcePending(false);
+  }
 
   bool isPending = false;
   nsresult rv = mChannel->IsPending(&isPending);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 
   // Resume only we suspended earlier.
   if (mSuspendedForDiversion) {
     mChannel->Resume();
   }
   // Channel has already sent OnStartRequest to the child, so ensure that we
   // call it here if it hasn't already been called.
   if (!mDivertedOnStartRequest) {
-    mChannel->ForcePending(true);
+    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+    if (forcePendingIChan) {
+      forcePendingIChan->ForcePending(true);
+    }
     mDivertToListener->OnStartRequest(mChannel, nullptr);
-    mChannel->ForcePending(false);
+
+    if (forcePendingIChan) {
+      forcePendingIChan->ForcePending(false);
+    }
   }
   // If the channel is pending, it will call OnStopRequest itself; otherwise, do
   // it here.
   if (!isPending) {
     mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
   }
   mDivertToListener = nullptr;
   mChannel = nullptr;
 
   if (!mIPCClosed) {
     unused << SendDeleteSelf();
   }
 }
 
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIChannelEventSink
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::AsyncOnChannelRedirect(
+                            nsIChannel *oldChannel,
+                            nsIChannel *newChannel,
+                            uint32_t redirectFlags,
+                            nsIAsyncVerifyRedirectCallback* callback)
+{
+  nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel);
+  if (!ftpChan) {
+    // when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel.
+    nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel);
+    if (!httpChan)
+      return NS_ERROR_UNEXPECTED; 
+  }
+  mChannel = newChannel;
+  callback->OnRedirectVerifyCallback(NS_OK);
+  return NS_OK; 
+}
+
 //---------------------
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/ftp/FTPChannelParent.h
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -19,23 +19,25 @@ class nsILoadContext;
 
 namespace mozilla {
 namespace net {
 
 class FTPChannelParent : public PFTPChannelParent
                        , public nsIParentChannel
                        , public nsIInterfaceRequestor
                        , public ADivertableParentChannel
+                       , public nsIChannelEventSink
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIPARENTCHANNEL
   NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSICHANNELEVENTSINK
 
   FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatus aOverrideStatus);
 
   bool Init(const FTPChannelCreationArgs& aOpenArgs);
 
   // ADivertableParentChannel functions.
   void DivertTo(nsIStreamListener *aListener) MOZ_OVERRIDE;
   nsresult SuspendForDiversion() MOZ_OVERRIDE;
@@ -72,17 +74,18 @@ protected:
   virtual bool RecvDivertOnDataAvailable(const nsCString& data,
                                          const uint64_t& offset,
                                          const uint32_t& count) MOZ_OVERRIDE;
   virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) MOZ_OVERRIDE;
   virtual bool RecvDivertComplete() MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
-  nsRefPtr<nsFtpChannel> mChannel;
+  // if configured to use HTTP proxy for FTP, this can an an HTTP channel.
+  nsCOMPtr<nsIChannel> mChannel;
 
   bool mIPCClosed;
 
   nsCOMPtr<nsILoadContext> mLoadContext;
 
   PBOverrideStatus mPBOverride;
 
   // If OnStart/OnData/OnStop have been diverted from the child, forward them to
--- a/netwerk/protocol/ftp/nsFTPChannel.cpp
+++ b/netwerk/protocol/ftp/nsFTPChannel.cpp
@@ -24,17 +24,18 @@ extern PRLogModuleInfo* gFTPLog;
 
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS_INHERITED(nsFtpChannel,
                             nsBaseChannel,
                             nsIUploadChannel,
                             nsIResumableChannel,
                             nsIFTPChannel,
-                            nsIProxiedChannel)
+                            nsIProxiedChannel,
+                            nsIForcePendingChannel)
 
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsFtpChannel::SetUploadStream(nsIInputStream *stream,
                               const nsACString &contentType,
                               int64_t contentLength)
 {
@@ -194,24 +195,26 @@ nsFtpChannel::GetFTPEventSink(nsCOMPtr<n
         GetCallback(ftpSink);
         if (ftpSink) {
             mFTPEventSink = new FTPEventSinkProxy(ftpSink);
         }
     }
     aResult = mFTPEventSink;
 }
 
-void
+NS_IMETHODIMP
 nsFtpChannel::ForcePending(bool aForcePending)
 {
     // Set true here so IsPending will return true.
     // Required for callback diversion from child back to parent. In such cases
     // OnStopRequest can be called in the parent before callbacks are diverted
     // back from the child to the listener in the parent.
     mForcePending = aForcePending;
+
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFtpChannel::IsPending(bool *result)
 {
   *result = Pending();
   return NS_OK;
 }
--- a/netwerk/protocol/ftp/nsFTPChannel.h
+++ b/netwerk/protocol/ftp/nsFTPChannel.h
@@ -7,28 +7,30 @@
 #ifndef nsFTPChannel_h___
 #define nsFTPChannel_h___
 
 #include "nsBaseChannel.h"
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIFTPChannel.h"
+#include "nsIForcePendingChannel.h"
 #include "nsIUploadChannel.h"
 #include "nsIProxyInfo.h"
 #include "nsIProxiedChannel.h"
 #include "nsIResumableChannel.h"
 
 class nsIURI;
 
 class nsFtpChannel : public nsBaseChannel,
                      public nsIFTPChannel,
                      public nsIUploadChannel,
                      public nsIResumableChannel,
-                     public nsIProxiedChannel
+                     public nsIProxiedChannel,
+                     public nsIForcePendingChannel
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIUPLOADCHANNEL
     NS_DECL_NSIRESUMABLECHANNEL
     NS_DECL_NSIPROXIEDCHANNEL
 
     nsFtpChannel(nsIURI *uri, nsIProxyInfo *pi)
@@ -83,18 +85,18 @@ public:
     // Data stream to upload
     nsIInputStream *UploadStream() {
         return mUploadStream;
     }
 
     // Helper function for getting the nsIFTPEventSink.
     void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
 
-public: /* Internal Necko use only. */
-    void ForcePending(bool aForcePending);
+public:
+    NS_IMETHOD ForcePending(bool aForcePending);
 
 protected:
     virtual ~nsFtpChannel() {}
     virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
                                        nsIChannel** channel);
     virtual bool GetStatusArg(nsresult status, nsString &statusArg);
     virtual void OnCallbacksChanged();
 
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2853,35 +2853,40 @@ Http2Session::DispatchOnTunnel(nsAHttpTr
 
   LOG3(("Http2Session::DispatchOnTunnel %p trans=%p", this, trans));
 
   aHttpTransaction->SetConnection(nullptr);
 
   // this transaction has done its work of setting up a tunnel, let
   // the connection manager queue it if necessary
   trans->SetDontRouteViaWildCard(true);
+  trans->EnableKeepAlive();
 
   if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
     LOG3(("Http2Session::DispatchOnTunnel %p create on new tunnel %s",
           this, ci->HashKey().get()));
+    // The connect transaction will hold onto the underlying http
+    // transaction so that an auth created by the connect can be mappped
+    // to the correct security callbacks
     nsRefPtr<SpdyConnectTransaction> connectTrans =
       new SpdyConnectTransaction(ci, aCallbacks,
                                  trans->Caps(), trans, this);
     AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
               false, nullptr);
     Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
     MOZ_ASSERT(tunnel);
     RegisterTunnel(tunnel);
+  } else {
+    // requeue it. The connection manager is responsible for actually putting
+    // this on the tunnel connection with the specific ci now that it
+    // has DontRouteViaWildCard set.
+    LOG3(("Http2Session::DispatchOnTunnel %p trans=%p queue in connection manager",
+          this, trans));
+    gHttpHandler->InitiateTransaction(trans, trans->Priority());
   }
-
-  // requeue it. The connection manager is responsible for actually putting
-  // this on the tunnel connection with the specific ci now that it
-  // has DontRouteViaWildCard set.
-  trans->EnableKeepAlive();
-  gHttpHandler->InitiateTransaction(trans, trans->Priority());
 }
 
 
 nsresult
 Http2Session::BufferOutput(const char *buf,
                            uint32_t count,
                            uint32_t *countRead)
 {
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -64,16 +64,17 @@ Http2Stream::Http2Stream(nsAHttpTransact
   , mTxStreamFrameSize(0)
   , mRequestBodyLenRemaining(0)
   , mLocalUnacked(0)
   , mBlockedOnRwin(false)
   , mTotalSent(0)
   , mTotalRead(0)
   , mPushSource(nullptr)
   , mIsTunnel(false)
+  , mPlainTextTunnel(false)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   LOG3(("Http2Stream::Http2Stream %p", this));
 
   mServerReceiveWindow = session->GetServerInitialStreamWindow();
   mClientReceiveWindow = session->PushAllowance();
 
@@ -214,17 +215,17 @@ Http2Stream::ReadSegments(nsAHttpSegment
     MOZ_ASSERT(false, "Http2Stream::ReadSegments unknown state");
     break;
   }
 
   return rv;
 }
 
 // WriteSegments() is used to read data off the socket. Generally this is
-// just a call through to the associate nsHttpTransaciton for this stream
+// just a call through to the associated nsHttpTransaction for this stream
 // for the remaining data bytes indicated by the current DATA frame.
 
 nsresult
 Http2Stream::WriteSegments(nsAHttpSegmentWriter *writer,
                            uint32_t count,
                            uint32_t *countWritten)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -857,35 +858,39 @@ Http2Stream::ConvertResponseHeaders(Http
   decompressor->GetStatus(status);
   if (status.IsEmpty()) {
     LOG3(("Http2Stream::ConvertHeaders %p Error - no status\n", this));
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
   if (mIsTunnel) {
     nsresult errcode;
-    if (status.ToInteger(&errcode) != 200) {
-      LOG3(("Http2Stream %p Tunnel not 200", this));
-      return NS_ERROR_ABORT;
+
+    int32_t code = status.ToInteger(&errcode);
+    LOG3(("Http2Stream %p Tunnel Response code %d", this, code));
+    if ((code / 100) != 2) {
+      MapStreamToPlainText();
     }
   }
 
   if (aHeadersIn.Length() && aHeadersOut.Length()) {
     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
     uint32_t ratio =
       aHeadersIn.Length() * 100 / aHeadersOut.Length();
     Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
   }
 
+  // The decoding went ok. Now we can customize and clean up.
+
   aHeadersIn.Truncate();
   aHeadersOut.Append("X-Firefox-Spdy: " NS_HTTP2_DRAFT_TOKEN "\r\n\r\n");
   LOG (("decoded response headers are:\n%s", aHeadersOut.BeginReading()));
-  if (mIsTunnel) {
+  if (mIsTunnel && !mPlainTextTunnel) {
     aHeadersOut.Truncate();
-    LOG(("SpdyStream3::ConvertHeaders %p 0x%X headers removed for tunnel\n",
+    LOG(("Http2Stream::ConvertHeaders %p 0x%X headers removed for tunnel\n",
          this, mStreamID));
   }
   return NS_OK;
 }
 
 // ConvertHeaders is used to convert the response headers
 // into HTTP/1 format and report some telemetry
 nsresult
@@ -1197,16 +1202,25 @@ Http2Stream::ClearTransactionsBlockedOnT
 
   if (!mIsTunnel) {
     return;
   }
   gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
 }
 
 void
+Http2Stream::MapStreamToPlainText()
+{
+  nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
+  MOZ_ASSERT(qiTrans);
+  mPlainTextTunnel = true;
+  qiTrans->ForcePlainText();
+}
+
+void
 Http2Stream::MapStreamToHttpConnection()
 {
   nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
   MOZ_ASSERT(qiTrans);
   qiTrans->MapStreamToHttpConnection(mSocketTransport,
                                      mTransaction->ConnectionInfo());
 }
 
--- a/netwerk/protocol/http/Http2Stream.h
+++ b/netwerk/protocol/http/Http2Stream.h
@@ -276,17 +276,19 @@ private:
   // For Http2Push
   Http2PushedStream *mPushSource;
 
 /// connect tunnels
 public:
   bool IsTunnel() { return mIsTunnel; }
 private:
   void ClearTransactionsBlockedOnTunnel();
+  void MapStreamToPlainText();
   void MapStreamToHttpConnection();
 
   bool mIsTunnel;
+  bool mPlainTextTunnel;
 };
 
 } // namespace mozilla::net
 } // namespace mozilla
 
 #endif // mozilla_net_Http2Stream_h
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -159,16 +159,17 @@ NS_IMPL_ADDREF(HttpBaseChannel)
 NS_IMPL_RELEASE(HttpBaseChannel)
 
 NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
   NS_INTERFACE_MAP_ENTRY(nsIRequest)
   NS_INTERFACE_MAP_ENTRY(nsIChannel)
   NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
   NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
   NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
+  NS_INTERFACE_MAP_ENTRY(nsIForcePendingChannel)
   NS_INTERFACE_MAP_ENTRY(nsIRedirectHistory)
   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
   NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
   NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
   NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
@@ -1633,16 +1634,28 @@ HttpBaseChannel::AddRedirect(nsIPrincipa
 
 NS_IMETHODIMP
 HttpBaseChannel::ForcePending(bool aForcePending)
 {
   mForcePending = aForcePending;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::GetLastModifiedTime(PRTime* lastModifiedTime)
+{
+  if (!mResponseHead)
+    return NS_ERROR_NOT_AVAILABLE;
+  uint32_t lastMod;
+  mResponseHead->GetLastModifiedValue(&lastMod);
+  *lastModifiedTime = lastMod;
+  return NS_OK;
+}
+
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsISupportsPriority
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetPriority(int32_t *value)
 {
   *value = mPriority;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -14,16 +14,17 @@
 #include "nsProxyInfo.h"
 #include "nsHttpRequestHead.h"
 #include "nsHttpResponseHead.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsIEncodedChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsIHttpChannelInternal.h"
+#include "nsIForcePendingChannel.h"
 #include "nsIRedirectHistory.h"
 #include "nsIUploadChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsIProgressEventSink.h"
 #include "nsIURI.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIStringEnumerator.h"
 #include "nsISupportsPriority.h"
@@ -58,16 +59,17 @@ class HttpBaseChannel : public nsHashPro
                       , public nsIRedirectHistory
                       , public nsIUploadChannel
                       , public nsIUploadChannel2
                       , public nsISupportsPriority
                       , public nsIResumableChannel
                       , public nsITraceableChannel
                       , public PrivateBrowsingChannel<HttpBaseChannel>
                       , public nsITimedChannel
+                      , public nsIForcePendingChannel
 {
 protected:
   virtual ~HttpBaseChannel();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIUPLOADCHANNEL
   NS_DECL_NSIUPLOADCHANNEL2
@@ -168,16 +170,17 @@ public:
   NS_IMETHOD SetLoadUnblocked(bool aLoadUnblocked);
   NS_IMETHOD GetApiRedirectToURI(nsIURI * *aApiRedirectToURI);
   NS_IMETHOD AddSecurityMessage(const nsAString &aMessageTag, const nsAString &aMessageCategory);
   NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages);
   NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable);
   NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable);
   NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect);
   NS_IMETHOD ForcePending(bool aForcePending);
+  NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime);
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener);
 
--- a/netwerk/protocol/http/SpdySession3.cpp
+++ b/netwerk/protocol/http/SpdySession3.cpp
@@ -2522,35 +2522,40 @@ SpdySession3::DispatchOnTunnel(nsAHttpTr
 
   LOG3(("SpdySession3::DispatchOnTunnel %p trans=%p", this, trans));
 
   aHttpTransaction->SetConnection(nullptr);
 
   // this transaction has done its work of setting up a tunnel, let
   // the connection manager queue it if necessary
   trans->SetDontRouteViaWildCard(true);
+  trans->EnableKeepAlive();
 
   if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
     LOG3(("SpdySession3::DispatchOnTunnel %p create on new tunnel %s",
           this, ci->HashKey().get()));
+    // The connect transaction will hold onto the underlying http
+    // transaction so that an auth created by the connect can be mappped
+    // to the correct security callbacks
     nsRefPtr<SpdyConnectTransaction> connectTrans =
       new SpdyConnectTransaction(ci, aCallbacks,
                                  trans->Caps(), trans, this);
     AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
               false, nullptr);
     SpdyStream3 *tunnel = mStreamTransactionHash.Get(connectTrans);
     MOZ_ASSERT(tunnel);
     RegisterTunnel(tunnel);
+  } else {
+    // requeue it. The connection manager is responsible for actually putting
+    // this on the tunnel connection with the specific ci now that it
+    // has DontRouteViaWildCard set.
+    LOG3(("SpdySession3::DispatchOnTunnel %p trans=%p queue in connection manager",
+          this, trans));
+    gHttpHandler->InitiateTransaction(trans, trans->Priority());
   }
-
-  // requeue it. The connection manager is responsible for actually putting
-  // this on the tunnel connection with the specific ci now that it
-  // has DontRouteViaWildCard set.
-  trans->EnableKeepAlive();
-  gHttpHandler->InitiateTransaction(trans, trans->Priority());
 }
 
 //-----------------------------------------------------------------------------
 // Modified methods of nsAHttpConnection
 //-----------------------------------------------------------------------------
 
 void
 SpdySession3::TransactionHasDataToWrite(nsAHttpTransaction *caller)
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -2654,35 +2654,40 @@ SpdySession31::DispatchOnTunnel(nsAHttpT
 
   LOG3(("SpdySession31::DispatchOnTunnel %p trans=%p", this, trans));
 
   aHttpTransaction->SetConnection(nullptr);
 
   // this transaction has done its work of setting up a tunnel, let
   // the connection manager queue it if necessary
   trans->SetDontRouteViaWildCard(true);
+  trans->EnableKeepAlive();
 
   if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
     LOG3(("SpdySession31::DispatchOnTunnel %p create on new tunnel %s",
           this, ci->HashKey().get()));
+    // The connect transaction will hold onto the underlying http
+    // transaction so that an auth created by the connect can be mappped
+    // to the correct security callbacks
     nsRefPtr<SpdyConnectTransaction> connectTrans =
       new SpdyConnectTransaction(ci, aCallbacks,
                                  trans->Caps(), trans, this);
     AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL,
               false, nullptr);
     SpdyStream31 *tunnel = mStreamTransactionHash.Get(connectTrans);
     MOZ_ASSERT(tunnel);
     RegisterTunnel(tunnel);
+  } else {
+    // requeue it. The connection manager is responsible for actually putting
+    // this on the tunnel connection with the specific ci now that it
+    // has DontRouteViaWildCard set.
+    LOG3(("SpdySession31::DispatchOnTunnel %p trans=%p queue in connection manager",
+          this, trans));
+    gHttpHandler->InitiateTransaction(trans, trans->Priority());
   }
-
-  // requeue it. The connection manager is responsible for actually putting
-  // this on the tunnel connection with the specific ci now that it
-  // has DontRouteViaWildCard set.
-  trans->EnableKeepAlive();
-  gHttpHandler->InitiateTransaction(trans, trans->Priority());
 }
 
 nsresult
 SpdySession31::BufferOutput(const char *buf,
                             uint32_t count,
                             uint32_t *countRead)
 {
   nsAHttpSegmentReader *old = mSegmentReader;
--- a/netwerk/protocol/http/SpdyStream3.cpp
+++ b/netwerk/protocol/http/SpdyStream3.cpp
@@ -67,16 +67,17 @@ SpdyStream3::SpdyStream3(nsAHttpTransact
   , mRequestBodyLenRemaining(0)
   , mPriority(priority)
   , mLocalUnacked(0)
   , mBlockedOnRwin(false)
   , mTotalSent(0)
   , mTotalRead(0)
   , mPushSource(nullptr)
   , mIsTunnel(false)
+  , mPlainTextTunnel(false)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   LOG3(("SpdyStream3::SpdyStream3 %p", this));
 
   mRemoteWindow = spdySession->GetServerInitialWindow();
   mLocalWindow = spdySession->PushAllowance();
 
@@ -189,17 +190,17 @@ SpdyStream3::ReadSegments(nsAHttpSegment
     MOZ_ASSERT(false, "SpdyStream3::ReadSegments unknown state");
     break;
   }
 
   return rv;
 }
 
 // WriteSegments() is used to read data off the socket. Generally this is
-// just a call through to the associate nsHttpTransaciton for this stream
+// just a call through to the associated nsHttpTransaction for this stream
 // for the remaining data bytes indicated by the current DATA frame.
 
 nsresult
 SpdyStream3::WriteSegments(nsAHttpSegmentWriter *writer,
                           uint32_t count,
                           uint32_t *countWritten)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -1272,26 +1273,28 @@ SpdyStream3::ConvertHeaders(nsACString &
       nvpair += 8 + nameLen + valueLen;
     }
 
     // move to the next name/value header block (if there is one) - the
     // first pair is offset 4 bytes into it
     nvpair += 4;
   } while (lastHeaderByte >= nvpair);
 
+  // The decoding went ok. Now we can customize and clean up.
+
   aHeadersOut.AppendLiteral("X-Firefox-Spdy: 3\r\n\r\n");
   LOG (("decoded response headers are:\n%s",
         aHeadersOut.BeginReading()));
 
   // The spdy formatted buffer isnt needed anymore - free it up
   mDecompressBuffer = nullptr;
   mDecompressBufferSize = 0;
   mDecompressBufferUsed = 0;
 
-  if (mIsTunnel) {
+  if (mIsTunnel && !mPlainTextTunnel) {
     aHeadersOut.Truncate();
     LOG(("SpdyStream3::ConvertHeaders %p 0x%X headers removed for tunnel\n",
          this, mStreamID));
   }
 
   return NS_OK;
 }
 
@@ -1366,28 +1369,28 @@ SpdyStream3::GetFullyOpen()
 }
 
 nsresult
 SpdyStream3::SetFullyOpen()
 {
   MOZ_ASSERT(!mFullyOpen);
   mFullyOpen = 1;
   if (mIsTunnel) {
+    int32_t code = 0;
     nsDependentCSubstring statusSubstring;
-    nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
-                             statusSubstring);
+    nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"), statusSubstring);
     if (NS_SUCCEEDED(rv)) {
       nsCString status(statusSubstring);
       nsresult errcode;
+      code = status.ToInteger(&errcode);
+    }
 
-      if (status.ToInteger(&errcode) != 200) {
-        LOG3(("SpdyStream3::SetFullyOpen %p Tunnel not 200", this));
-        return NS_ERROR_FAILURE;
-      }
-      LOG3(("SpdyStream3::SetFullyOpen %p Tunnel 200 OK", this));
+    LOG3(("SpdyStream3::SetFullyOpen %p Tunnel Response code %d", this, code));
+    if ((code / 100) != 2) {
+      MapStreamToPlainText();
     }
 
     MapStreamToHttpConnection();
     ClearTransactionsBlockedOnTunnel();
   }
   return NS_OK;
 }
 
@@ -1557,16 +1560,25 @@ SpdyStream3::ClearTransactionsBlockedOnT
 
   if (!mIsTunnel) {
     return;
   }
   gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
 }
 
 void
+SpdyStream3::MapStreamToPlainText()
+{
+  nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
+  MOZ_ASSERT(qiTrans);
+  mPlainTextTunnel = true;
+  qiTrans->ForcePlainText();
+}
+
+void
 SpdyStream3::MapStreamToHttpConnection()
 {
   nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
   MOZ_ASSERT(qiTrans);
   qiTrans->MapStreamToHttpConnection(mSocketTransport,
                                      mTransaction->ConnectionInfo());
 }
 
--- a/netwerk/protocol/http/SpdyStream3.h
+++ b/netwerk/protocol/http/SpdyStream3.h
@@ -252,16 +252,18 @@ private:
   // For SpdyPush
   SpdyPushedStream3 *mPushSource;
 
 /// connect tunnels
 public:
   bool IsTunnel() { return mIsTunnel; }
 private:
   void ClearTransactionsBlockedOnTunnel();
+  void MapStreamToPlainText();
   void MapStreamToHttpConnection();
 
   bool mIsTunnel;
+  bool mPlainTextTunnel;
 };
 
 }} // namespace mozilla::net
 
 #endif // mozilla_net_SpdyStream3_h
--- a/netwerk/protocol/http/SpdyStream31.cpp
+++ b/netwerk/protocol/http/SpdyStream31.cpp
@@ -65,16 +65,17 @@ SpdyStream31::SpdyStream31(nsAHttpTransa
   , mRequestBodyLenRemaining(0)
   , mPriority(priority)
   , mLocalUnacked(0)
   , mBlockedOnRwin(false)
   , mTotalSent(0)
   , mTotalRead(0)
   , mPushSource(nullptr)
   , mIsTunnel(false)
+  , mPlainTextTunnel(false)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   LOG3(("SpdyStream31::SpdyStream31 %p", this));
 
   mRemoteWindow = spdySession->GetServerInitialStreamWindow();
   mLocalWindow = spdySession->PushAllowance();
 
@@ -194,17 +195,17 @@ SpdyStream31::ReadSegments(nsAHttpSegmen
     MOZ_ASSERT(false, "SpdyStream31::ReadSegments unknown state");
     break;
   }
 
   return rv;
 }
 
 // WriteSegments() is used to read data off the socket. Generally this is
-// just a call through to the associate nsHttpTransaciton for this stream
+// just a call through to the associated nsHttpTransaction for this stream
 // for the remaining data bytes indicated by the current DATA frame.
 
 nsresult
 SpdyStream31::WriteSegments(nsAHttpSegmentWriter *writer,
                             uint32_t count,
                             uint32_t *countWritten)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -1288,26 +1289,28 @@ SpdyStream31::ConvertHeaders(nsACString 
       nvpair += 8 + nameLen + valueLen;
     }
 
     // move to the next name/value header block (if there is one) - the
     // first pair is offset 4 bytes into it
     nvpair += 4;
   } while (lastHeaderByte >= nvpair);
 
+  // The decoding went ok. Now we can customize and clean up.
+
   aHeadersOut.AppendLiteral("X-Firefox-Spdy: 3.1\r\n\r\n");
   LOG (("decoded response headers are:\n%s",
         aHeadersOut.BeginReading()));
 
   // The spdy formatted buffer isnt needed anymore - free it up
   mDecompressBuffer = nullptr;
   mDecompressBufferSize = 0;
   mDecompressBufferUsed = 0;
 
-  if (mIsTunnel) {
+  if (mIsTunnel && !mPlainTextTunnel) {
     aHeadersOut.Truncate();
     LOG(("SpdyStream31::ConvertHeaders %p 0x%X headers removed for tunnel\n",
          this, mStreamID));
   }
 
   return NS_OK;
 }
 
@@ -1380,28 +1383,28 @@ SpdyStream31::GetFullyOpen()
 }
 
 nsresult
 SpdyStream31::SetFullyOpen()
 {
   MOZ_ASSERT(!mFullyOpen);
   mFullyOpen = 1;
   if (mIsTunnel) {
+    int32_t code = 0;
     nsDependentCSubstring statusSubstring;
-    nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
-                             statusSubstring);
+    nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"), statusSubstring);
     if (NS_SUCCEEDED(rv)) {
       nsCString status(statusSubstring);
       nsresult errcode;
+      code = status.ToInteger(&errcode);
+    }
 
-      if (status.ToInteger(&errcode) != 200) {
-        LOG3(("SpdyStream31::SetFullyOpen %p Tunnel not 200", this));
-        return NS_ERROR_FAILURE;
-      }
-      LOG3(("SpdyStream31::SetFullyOpen %p Tunnel 200 OK", this));
+    LOG3(("SpdyStream31::SetFullyOpen %p Tunnel Response code %d", this, code));
+    if ((code / 100) != 2) {
+      MapStreamToPlainText();
     }
 
     MapStreamToHttpConnection();
     ClearTransactionsBlockedOnTunnel();
   }
   return NS_OK;
 }
 
@@ -1581,16 +1584,25 @@ SpdyStream31::ClearTransactionsBlockedOn
 
   if (!mIsTunnel) {
     return;
   }
   gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
 }
 
 void
+SpdyStream31::MapStreamToPlainText()
+{
+  nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
+  MOZ_ASSERT(qiTrans);
+  mPlainTextTunnel = true;
+  qiTrans->ForcePlainText();
+}
+
+void
 SpdyStream31::MapStreamToHttpConnection()
 {
   nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
   MOZ_ASSERT(qiTrans);
   qiTrans->MapStreamToHttpConnection(mSocketTransport,
                                      mTransaction->ConnectionInfo());
 }
 
--- a/netwerk/protocol/http/SpdyStream31.h
+++ b/netwerk/protocol/http/SpdyStream31.h
@@ -247,16 +247,18 @@ private:
   // For SpdyPush
   SpdyPushedStream31 *mPushSource;
 
 /// connect tunnels
 public:
   bool IsTunnel() { return mIsTunnel; }
 private:
   void ClearTransactionsBlockedOnTunnel();
+  void MapStreamToPlainText();
   void MapStreamToHttpConnection();
 
   bool mIsTunnel;
+  bool mPlainTextTunnel;
 };
 
 }} // namespace mozilla::net
 
 #endif // mozilla_net_SpdyStream31_h
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -10,16 +10,17 @@
 #include "Http2Session.h"
 #include "nsHttp.h"
 #include "nsHttpHandler.h"
 #include "nsHttpRequestHead.h"
 #include "nsISocketProvider.h"
 #include "nsISocketProviderService.h"
 #include "nsISSLSocketControl.h"
 #include "nsISocketTransport.h"
+#include "nsISupportsPriority.h"
 #include "nsNetAddr.h"
 #include "prerror.h"
 #include "prio.h"
 #include "TunnelUtils.h"
 
 #ifdef DEBUG
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
@@ -820,42 +821,62 @@ private:
   nsWeakPtr mWeakTrans; // SpdyConnectTransaction *
   nsIInputStreamCallback *mCallback;
   nsresult mStatus;
 };
 
 SpdyConnectTransaction::SpdyConnectTransaction(nsHttpConnectionInfo *ci,
                                                nsIInterfaceRequestor *callbacks,
                                                uint32_t caps,
-                                               nsAHttpTransaction *trans,
+                                               nsHttpTransaction *trans,
                                                nsAHttpConnection *session)
   : NullHttpTransaction(ci, callbacks, caps | NS_HTTP_ALLOW_KEEPALIVE)
   , mConnectStringOffset(0)
   , mSession(session)
   , mSegmentReader(nullptr)
   , mInputDataSize(0)
   , mInputDataUsed(0)
   , mInputDataOffset(0)
   , mOutputDataSize(0)
   , mOutputDataUsed(0)
   , mOutputDataOffset(0)
+  , mForcePlainText(false)
 {
   LOG(("SpdyConnectTransaction ctor %p\n", this));
 
   mTimestampSyn = TimeStamp::Now();
   mRequestHead = new nsHttpRequestHead();
   nsHttpConnection::MakeConnectString(trans, mRequestHead, mConnectString);
+  mDrivingTransaction = trans;
 }
 
 SpdyConnectTransaction::~SpdyConnectTransaction()
 {
   LOG(("SpdyConnectTransaction dtor %p\n", this));
   if (mRequestHead) {
     delete mRequestHead;
   }
+
+  if (mDrivingTransaction) {
+    // requeue it I guess. This should be gone.
+    gHttpHandler->InitiateTransaction(mDrivingTransaction,
+                                      mDrivingTransaction->Priority());
+  }
+}
+
+void
+SpdyConnectTransaction::ForcePlainText()
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+  MOZ_ASSERT(!mInputDataUsed && !mInputDataSize && !mInputDataOffset);
+  MOZ_ASSERT(!mForcePlainText);
+  MOZ_ASSERT(!mTunnelTransport, "call before mapstreamtohttpconnection");
+
+  mForcePlainText = true;
+  return;
 }
 
 void
 SpdyConnectTransaction::MapStreamToHttpConnection(nsISocketTransport *aTransport,
                                                   nsHttpConnectionInfo *aConnInfo)
 {
   mConnInfo = aConnInfo;
 
@@ -875,20 +896,33 @@ SpdyConnectTransaction::MapStreamToHttpC
   MOZ_ASSERT(aConnInfo->UsingHttpsProxy());
   TimeDuration rtt = TimeStamp::Now() - mTimestampSyn;
   mTunneledConn->Init(aConnInfo,
                       gHttpHandler->ConnMgr()->MaxRequestDelay(),
                       mTunnelTransport, mTunnelStreamIn, mTunnelStreamOut,
                       true, callbacks,
                       PR_MillisecondsToInterval(
                         static_cast<uint32_t>(rtt.ToMilliseconds())));
-  mTunneledConn->SetupSecondaryTLS();
-  mTunneledConn->SetInSpdyTunnel(true);
+  if (mForcePlainText) {
+      mTunneledConn->ForcePlainText();
+  } else {
+    mTunneledConn->SetupSecondaryTLS();
+    mTunneledConn->SetInSpdyTunnel(true);
+  }
 
-  gHttpHandler->ConnMgr()->ReclaimConnection(mTunneledConn);
+  // make the originating transaction stick to the tunneled conn
+  nsRefPtr<nsAHttpConnection> wrappedConn =
+    gHttpHandler->ConnMgr()->MakeConnectionHandle(mTunneledConn);
+  mDrivingTransaction->SetConnection(wrappedConn);
+  mDrivingTransaction->MakeSticky();
+
+  // jump the priority and start the dispatcher
+  gHttpHandler->InitiateTransaction(
+    mDrivingTransaction, nsISupportsPriority::PRIORITY_HIGHEST - 60);
+  mDrivingTransaction = nullptr;
 }
 
 nsresult
 SpdyConnectTransaction::Flush(uint32_t count, uint32_t *countRead)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   LOG(("SpdyConnectTransaction::Flush %p count %d avail %d\n",
        this, count, mOutputDataUsed - mOutputDataOffset));
@@ -928,17 +962,18 @@ SpdyConnectTransaction::Flush(uint32_t c
 }
 
 nsresult
 SpdyConnectTransaction::ReadSegments(nsAHttpSegmentReader *reader,
                                      uint32_t count,
                                      uint32_t *countRead)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-  LOG(("SpdyConnectTransaction::ReadSegments %p count %d\n", this, count));
+  LOG(("SpdyConnectTransaction::ReadSegments %p count %d conn %p\n",
+       this, count, mTunneledConn.get()));
 
   mSegmentReader = reader;
 
   // spdy stream carrying tunnel is not setup yet.
   if (!mTunneledConn) {
     uint32_t toWrite = mConnectString.Length() - mConnectStringOffset;
     toWrite = std::min(toWrite, count);
     *countRead = toWrite;
@@ -957,16 +992,27 @@ SpdyConnectTransaction::ReadSegments(nsA
           mConnectStringOffset = 0;
         }
       }
       return rv;
     }
     return NS_BASE_STREAM_WOULD_BLOCK;
   }
 
+  if (mForcePlainText) {
+    // this path just ignores sending the request so that we can
+    // send a synthetic reply in writesegments()
+    LOG(("SpdyConnectTransaciton::ReadSegments %p dropping %d output bytes "
+         "due to synthetic reply\n", this, mOutputDataUsed - mOutputDataOffset));
+    *countRead = mOutputDataUsed - mOutputDataOffset;
+    mOutputDataOffset = mOutputDataUsed = 0;
+    mTunneledConn->DontReuse();
+    return NS_OK;
+  }
+
   *countRead = 0;
   Flush(count, countRead);
   if (!mTunnelStreamOut->mCallback) {
     return NS_BASE_STREAM_WOULD_BLOCK;
   }
 
   nsresult rv =
     mTunnelStreamOut->mCallback->OnOutputStreamReady(mTunnelStreamOut);
@@ -1015,17 +1061,17 @@ SpdyConnectTransaction::WriteSegments(ns
 
   // first call into the tunnel stream to get the demux'd data out of the
   // spdy session.
   EnsureBuffer(mInputData, mInputDataUsed + count, mInputDataUsed, mInputDataSize);
   nsresult rv = writer->OnWriteSegment(mInputData + mInputDataUsed,
                                        count, countWritten);
   if (NS_FAILED(rv)) {
     if (rv != NS_BASE_STREAM_WOULD_BLOCK) {
-      LOG(("SpdyConnectTransaction::Flush %p Error %x\n", this, rv));
+      LOG(("SpdyConnectTransaction::WriteSegments wrapped writer %p Error %x\n", this, rv));
       CreateShimError(rv);
     }
     return rv;
   }
   mInputDataUsed += *countWritten;
   LOG(("SpdyConnectTransaction %p %d new bytes [%d total] of ciphered data buffered\n",
        this, *countWritten, mInputDataUsed - mInputDataOffset));
 
--- a/netwerk/protocol/http/TunnelUtils.h
+++ b/netwerk/protocol/http/TunnelUtils.h
@@ -170,22 +170,25 @@ class nsHttpConnection;
 class ASpdySession;
 
 class SpdyConnectTransaction MOZ_FINAL : public NullHttpTransaction
 {
 public:
   SpdyConnectTransaction(nsHttpConnectionInfo *ci,
                          nsIInterfaceRequestor *callbacks,
                          uint32_t caps,
-                         nsAHttpTransaction *trans,
+                         nsHttpTransaction *trans,
                          nsAHttpConnection *session);
   ~SpdyConnectTransaction();
 
   SpdyConnectTransaction *QuerySpdyConnectTransaction() { return this; }
 
+  // A transaction is forced into plaintext when it is intended to be used as a CONNECT
+  // tunnel but the setup fails. The plaintext only carries the CONNECT error.
+  void ForcePlainText();
   void MapStreamToHttpConnection(nsISocketTransport *aTransport,
                                  nsHttpConnectionInfo *aConnInfo);
 
   nsresult ReadSegments(nsAHttpSegmentReader *reader,
                         uint32_t count, uint32_t *countRead) MOZ_OVERRIDE MOZ_FINAL;
   nsresult WriteSegments(nsAHttpSegmentWriter *writer,
                          uint32_t count, uint32_t *countWritten) MOZ_OVERRIDE MOZ_FINAL;
   nsHttpRequestHead *RequestHead() MOZ_OVERRIDE MOZ_FINAL;
@@ -210,24 +213,26 @@ private:
   uint32_t             mInputDataUsed;
   uint32_t             mInputDataOffset;
 
   nsAutoArrayPtr<char> mOutputData;
   uint32_t             mOutputDataSize;
   uint32_t             mOutputDataUsed;
   uint32_t             mOutputDataOffset;
 
+  bool                           mForcePlainText;
   TimeStamp                      mTimestampSyn;
   nsRefPtr<nsHttpConnectionInfo> mConnInfo;
 
   // mTunneledConn, mTunnelTransport, mTunnelStreamIn, mTunnelStreamOut
   // are the connectors to the "real" http connection. They are created
   // together when the tunnel setup is complete and a static reference is held
   // for the lifetime of the tunnel.
   nsRefPtr<nsHttpConnection>     mTunneledConn;
   nsRefPtr<SocketTransportShim>  mTunnelTransport;
   nsRefPtr<InputStreamShim>      mTunnelStreamIn;
   nsRefPtr<OutputStreamShim>     mTunnelStreamOut;
+  nsRefPtr<nsHttpTransaction>    mDrivingTransaction;
 };
 
 }} // namespace mozilla::net
 
 #endif // mozilla_net_TLSFilterTransaction_h
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.h
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.h
@@ -43,17 +43,17 @@ private:
     int32_t     ProxyPort() const
     { return mProxyInfo ? mProxyInfo->Port() : -1; }
 
     const char *Host() const      { return mHost.get(); }
     int32_t     Port() const      { return mPort; }
     bool        UsingSSL() const  { return mUsingSSL; }
 
     bool        UsingHttpProxy() const
-    { return !!(mProxyInfo && !nsCRT::strcmp(mProxyInfo->Type(), "http")); }
+    { return mProxyInfo && (mProxyInfo->IsHTTP() || mProxyInfo->IsHTTPS()); }
 
     nsresult PrepareForAuthentication(bool proxyAuth);
     nsresult GenCredsAndSetEntry(nsIHttpAuthenticator *, bool proxyAuth,
                                  const char *scheme, const char *host,
                                  int32_t port, const char *dir,
                                  const char *realm, const char *challenge,
                                  const nsHttpAuthIdentity &ident,
                                  nsCOMPtr<nsISupports> &session, char **result);
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -63,16 +63,17 @@ nsHttpConnection::nsHttpConnection()
     , mSupportsPipelining(false) // assume low-grade server
     , mIsReused(false)
     , mCompletedProxyConnect(false)
     , mLastTransactionExpectedNoContent(false)
     , mIdleMonitoring(false)
     , mProxyConnectInProgress(false)
     , mExperienced(false)
     , mInSpdyTunnel(false)
+    , mForcePlainText(false)
     , mHttp1xTransactionCount(0)
     , mRemainingConnectionUses(0xffffffff)
     , mClassification(nsAHttpTransaction::CLASS_GENERAL)
     , mNPNComplete(false)
     , mSetupSSLCalled(false)
     , mUsingSpdyVersion(0)
     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
     , mReportedSpdy(false)
@@ -455,17 +456,17 @@ nsHttpConnection::SetupSSL()
 
     if (mNPNComplete)
         return;
 
     // we flip this back to false if SetNPNList succeeds at the end
     // of this function
     mNPNComplete = true;
 
-    if (!mConnInfo->FirstHopSSL()) {
+    if (!mConnInfo->FirstHopSSL() || mForcePlainText) {
         return;
     }
 
     // if we are connected to the proxy with TLS, start the TLS
     // flow immediately without waiting for a CONNECT sequence.
     if (mInSpdyTunnel) {
         InitSSLParams(false, true);
     } else {
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -106,29 +106,36 @@ public:
         mLastTransactionExpectedNoContent = val;
     }
 
     bool NeedSpdyTunnel()
     {
         return mConnInfo->UsingHttpsProxy() && !mTLSFilter && mConnInfo->UsingConnect();
     }
 
+    // A connection is forced into plaintext when it is intended to be used as a CONNECT
+    // tunnel but the setup fails. The plaintext only carries the CONNECT error.
+    void ForcePlainText()
+    {
+        mForcePlainText = true;
+    }
+
     nsISocketTransport   *Transport()      { return mSocketTransport; }
     nsAHttpTransaction   *Transaction()    { return mTransaction; }
     nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
 
     // nsAHttpConnection compatible methods (non-virtual):
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset);
     void     CloseTransaction(nsAHttpTransaction *, nsresult reason);
     void     GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnInfo); }
     nsresult TakeTransport(nsISocketTransport **,
                            nsIAsyncInputStream **,
                            nsIAsyncOutputStream **);
     void     GetSecurityInfo(nsISupports **);
-    bool     IsPersistent() { return IsKeepAlive(); }
+    bool     IsPersistent() { return IsKeepAlive() && !mDontReuse; }
     bool     IsReused();
     void     SetIsReusedAfter(uint32_t afterMilliseconds);
     nsresult PushBack(const char *data, uint32_t length);
     nsresult ResumeSend();
     nsresult ResumeRecv();
     int64_t  MaxBytesRead() {return mMaxBytesRead;}
     uint8_t GetLastHttpResponseVersion() { return mLastHttpResponseVersion; }
 
@@ -278,16 +285,17 @@ private:
     bool                            mSupportsPipelining;
     bool                            mIsReused;
     bool                            mCompletedProxyConnect;
     bool                            mLastTransactionExpectedNoContent;
     bool                            mIdleMonitoring;
     bool                            mProxyConnectInProgress;
     bool                            mExperienced;
     bool                            mInSpdyTunnel;
+    bool                            mForcePlainText;
 
     // The number of <= HTTP/1.1 transactions performed on this connection. This
     // excludes spdy transactions.
     uint32_t                        mHttp1xTransactionCount;
 
     // Keep-Alive: max="mRemainingConnectionUses" provides the number of future
     // transactions (including the current one) that the server expects to allow
     // on this persistent connection.
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -1979,20 +1979,31 @@ nsHttpConnectionMgr::ProcessNewTransacti
 
     nsAHttpConnection *wrappedConnection = trans->Connection();
     nsRefPtr<nsHttpConnection> conn;
     if (wrappedConnection)
         conn = dont_AddRef(wrappedConnection->TakeHttpConnection());
 
     if (conn) {
         MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
-        MOZ_ASSERT(((int32_t)ent->mActiveConns.IndexOf(conn)) != -1,
-                   "Sticky Connection Not In Active List");
         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
              "sticky connection=%p\n", trans, conn.get()));
+
+        if (static_cast<int32_t>(ent->mActiveConns.IndexOf(conn)) == -1) {
+            LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
+                 "sticky connection=%p needs to go on the active list\n", trans, conn.get()));
+
+            // make sure it isn't on the idle list - we expect this to be an
+            // unknown fresh connection
+            MOZ_ASSERT(static_cast<int32_t>(ent->mIdleConns.IndexOf(conn)) == -1);
+            MOZ_ASSERT(!conn->IsExperienced());
+
+            AddActiveConn(conn, ent); // make it active
+        }
+
         trans->SetConnection(nullptr);
         rv = DispatchTransaction(ent, trans, conn);
     } else {
         rv = TryDispatchTransaction(ent, trans->DontRouteViaWildCard(), trans);
     }
 
     if (NS_SUCCEEDED(rv)) {
         LOG(("  ProcessNewTransaction Dispatch Immediately trans=%p\n", trans));
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -400,16 +400,22 @@ private:
     public:
         NS_DECL_THREADSAFE_ISUPPORTS
         NS_DECL_NSAHTTPCONNECTION(mConn)
 
         nsConnectionHandle(nsHttpConnection *conn) { NS_ADDREF(mConn = conn); }
 
         nsHttpConnection *mConn;
     };
+public:
+    static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped)
+    {
+        return new nsConnectionHandle(aWrapped);
+    }
+private:
 
     // nsHalfOpenSocket is used to hold the state of an opening TCP socket
     // while we wait for it to establish and bind it to a connection
 
     class nsHalfOpenSocket MOZ_FINAL : public nsIOutputStreamCallback,
                                        public nsITransportEventSink,
                                        public nsIInterfaceRequestor,
                                        public nsITimerCallback
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -104,16 +104,17 @@ public:
     // setting mDontRouteViaWildCard to true means the transaction should only
     // be dispatched on a specific ConnectionInfo Hash Key (as opposed to a
     // generic wild card one). That means in the specific case of carrying this
     // transaction on an HTTP/2 tunnel it will only be dispatched onto an
     // existing tunnel instead of triggering creation of a new one.
     void SetDontRouteViaWildCard(bool var) { mDontRouteViaWildCard = var; }
     bool DontRouteViaWildCard() { return mDontRouteViaWildCard; }
     void EnableKeepAlive() { mCaps |= NS_HTTP_ALLOW_KEEPALIVE; }
+    void MakeSticky() { mCaps |= NS_HTTP_STICKY_CONNECTION; }
 
     // SetPriority() may only be used by the connection manager.
     void    SetPriority(int32_t priority) { mPriority = priority; }
     int32_t    Priority()                 { return mPriority; }
 
     const TimingStruct& Timings() const { return mTimings; }
     enum Classifier Classification() { return mClassification; }
 
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -195,15 +195,10 @@ interface nsIHttpChannelInternal : nsISu
     readonly attribute nsIURI apiRedirectToURI;
 
     /**
      * Add a new nsIPrincipal to the redirect chain. This is the only way to
      * write to nsIRedirectHistory.redirects.
      */
     void addRedirect(in nsIPrincipal aPrincipal);
 
-  /**
-   * ForcePending(true) overrides the normal behavior for the 
-   * channel's IsPending(), forcing 'true' to be returned. A call to
-   * ForcePending(false) reverts IsPending() back to normal behavior.
-   */
-    void ForcePending(in boolean aForcePending);
+    readonly attribute PRTime lastModifiedTime;
 };
--- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp
@@ -12,17 +12,17 @@
 #include "nsIPrefBranch.h"
 
 #include "nsCRT.h"
 
 #include "nsIMIMEService.h"
 
 #include "nsIViewSourceChannel.h"
 #include "nsIHttpChannel.h"
-#include "nsIHttpChannelInternal.h"
+#include "nsIForcePendingChannel.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 
 
 #define MAX_BUFFER_SIZE 512
 
 nsUnknownDecoder::nsUnknownDecoder()
   : mBuffer(nullptr)
@@ -199,30 +199,30 @@ nsUnknownDecoder::OnStopRequest(nsIReque
   // Analyze the buffer now...
   //
   if (mContentType.IsEmpty()) {
     DetermineContentType(request);
 
     // Make sure channel listeners see channel as pending while we call 
     // OnStartRequest/OnDataAvailable, even though the underlying channel 
     // has already hit OnStopRequest.
-    nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(request);
-    if (httpChannel) {
-      httpChannel->ForcePending(true);
+    nsCOMPtr<nsIForcePendingChannel> forcePendingChannel = do_QueryInterface(request);
+    if (forcePendingChannel) {
+      forcePendingChannel->ForcePending(true);
     }
 
     rv = FireListenerNotifications(request, aCtxt);
 
     if (NS_FAILED(rv)) {
       aStatus = rv;
     }
 
     // now we need to set pending state to false before calling OnStopRequest
-    if (httpChannel) {
-      httpChannel->ForcePending(false);
+    if (forcePendingChannel) {
+      forcePendingChannel->ForcePending(false);
     }
   }
 
   rv = mNextListener->OnStopRequest(request, aCtxt, aStatus);
   mNextListener = 0;
 
   return rv;
 }
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -93,19 +93,26 @@ function isObjectOrArray(obj) {
 //   non-privileged method that returns only strings, we can just waive Xray
 //   for that case.
 //
 // * We implement Xrays to pure JS [[Object]] and [[Array]] instances that
 //   filter out tricky things like callables. This is the right thing for
 //   security in general, but tends to break tests that try to pass object
 //   literals into SpecialPowers. So we waive [[Object]] and [[Array]]
 //   instances before inspecting properties.
+//
+// * When we don't have meaningful Xray semantics, we create an Opaque
+//   XrayWrapper for security reasons. For test code, we generally want to see
+//   through that sort of thing.
 function waiveXraysIfAppropriate(obj, propName) {
-  if (propName == 'toString' || isObjectOrArray(obj))
+  if (propName == 'toString' || isObjectOrArray(obj) ||
+      /Opaque/.test(Object.prototype.toString.call(obj)))
+{
     return XPCNativeWrapper.unwrap(obj);
+}
   return obj;
 }
 
 function callGetOwnPropertyDescriptor(obj, name) {
   obj = waiveXraysIfAppropriate(obj, name);
 
   // Quickstubbed getters and setters are propertyOps, and don't get reified
   // until someone calls __lookupGetter__ or __lookupSetter__ on them (note
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3694,28 +3694,31 @@ DebuggerServer.ObjectActorPreviewers = {
     };
 
     if (threadActor._gripDepth > 1) {
       return true;
     }
 
     let raw = obj.unsafeDereference();
     let entries = aGrip.preview.entries = [];
-    // We don't have Xrays to Iterators, so .entries returns [key, value]
-    // Arrays that live in content. But since we have Array Xrays,
-    // we'll deny access depending on the nature of those values. So we need
-    // to waive Xrays on those tuples (and re-apply them on the underlying
-    // values) until we fix bug 1023984.
+    // Iterating over a Map via .entries goes through various intermediate
+    // objects - an Iterator object, then a 2-element Array object, then the
+    // actual values we care about. We don't have Xrays to Iterator objects,
+    // so we get Opaque wrappers for them. And even though we have Xrays to
+    // Arrays, the semantics often deny access to the entires based on the
+    // nature of the values. So we need waive Xrays for the iterator object
+    // and the tupes, and then re-apply them on the underlying values until
+    // we fix bug 1023984.
     //
     // Even then though, we might want to continue waiving Xrays here for the
     // same reason we do so for Arrays above - this filtering behavior is likely
     // to be more confusing than beneficial in the case of Object previews.
-    for (let keyValuePair of Map.prototype.entries.call(raw)) {
-      let key = Cu.unwaiveXrays(Cu.waiveXrays(keyValuePair)[0]);
-      let value = Cu.unwaiveXrays(Cu.waiveXrays(keyValuePair)[1]);
+    for (let keyValuePair of Cu.waiveXrays(Map.prototype.entries.call(raw))) {
+      let key = Cu.unwaiveXrays(keyValuePair[0]);
+      let value = Cu.unwaiveXrays(keyValuePair[1]);
       key = makeDebuggeeValueIfNeeded(obj, key);
       value = makeDebuggeeValueIfNeeded(obj, value);
       entries.push([threadActor.createValueGrip(key),
                     threadActor.createValueGrip(value)]);
       if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
         break;
       }
     }
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1583,24 +1583,18 @@ NS_IMETHODIMP nsAndroidBridge::HandleGec
     }
 
     // Now handle legacy JSON messages.
     if (!val.isString()) {
         return NS_ERROR_INVALID_ARG;
     }
     JS::RootedString jsonStr(cx, val.toString());
 
-    size_t strLen = 0;
-    const jschar* strChar = JS_GetStringCharsAndLength(cx, jsonStr, &strLen);
-    if (!strChar) {
-        return NS_ERROR_UNEXPECTED;
-    }
-
     JS::RootedValue jsonVal(cx);
-    if (!JS_ParseJSON(cx, strChar, strLen, &jsonVal) || !jsonVal.isObject()) {
+    if (!JS_ParseJSON(cx, jsonStr, &jsonVal) || !jsonVal.isObject()) {
         return NS_ERROR_INVALID_ARG;
     }
 
     // Spit out a warning before sending the message.
     nsContentUtils::ReportToConsoleNonLocalized(
         NS_LITERAL_STRING("Use of JSON is deprecated. "
             "Please pass Javascript objects directly to handleGeckoMessage."),
         nsIScriptError::warningFlag,
--- a/xpcom/base/nsIException.idl
+++ b/xpcom/base/nsIException.idl
@@ -5,29 +5,34 @@
 
 /*
  * Interfaces for representing cross-language exceptions and stack traces.
  */
 
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(3bc4793f-e6be-44d6-b839-d6b9e85e5346)]
+[scriptable, uuid(13b75be1-f950-497b-81e4-a0214a14e5ae)]
 interface nsIStackFrame : nsISupports
 {
     // see nsIProgrammingLanguage for list of language consts
     readonly attribute uint32_t                language;
     readonly attribute AUTF8String             languageName;
     readonly attribute AString                 filename;
     readonly attribute AString                 name;
     // Valid line numbers begin at '1'. '0' indicates unknown.
     readonly attribute int32_t                 lineNumber;
     readonly attribute AUTF8String             sourceLine;
     readonly attribute nsIStackFrame           caller;
 
+    // Returns a formatted stack string that looks like the sort of
+    // string that would be returned by .stack on JS Error objects.
+    // Only works on JS-language stack frames.
+    readonly attribute AString                 formattedStack;
+
     AUTF8String toString();
 };
 
 [scriptable, uuid(1caf1461-be1d-4b79-a552-5292b6bf3c35)]
 interface nsIException : nsISupports
 {
     // A custom message set by the thrower.
     [binaryname(MessageMoz)] readonly attribute AUTF8String message;