Merge latest green birch changeset and mozilla-central
authorEd Morley <emorley@mozilla.com>
Fri, 14 Jun 2013 09:55:54 +0100
changeset 146532 65dcf8989992747c48386f428bde28dc0ca04bf5
parent 146531 36a8602bf34bc0e020e03dc86a42f9637507fec6 (current diff)
parent 146514 b7dbf40666b1df094f63831b1e99823234b7915c (diff)
child 146535 be23c06aab961789295ff91d7c47097160650f48
child 146540 710931b291bbfa8d8f0f47796c4b424c58580f37
child 146560 52c875b9c520f19cbe9ed33f7cc11be689bc8d61
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.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 latest green birch changeset and mozilla-central
js/src/jsapi-tests/testVersion.cpp
js/xpconnect/tests/unit/bug596580_versioned.js
js/xpconnect/tests/unit/test_bug596580.js
--- a/browser/base/content/test/browser_datareporting_notification.js
+++ b/browser/base/content/test/browser_datareporting_notification.js
@@ -48,25 +48,27 @@ function waitForNotificationClose(notifi
         cb();
       }
     }
   });
 
   observer.observe(parent, {childList: true});
 }
 
+let dumpAppender, rootLogger;
+
 function test() {
   waitForExplicitFinish();
 
   let ns = {};
   Components.utils.import("resource://services-common/log4moz.js", ns);
-  let rootLogger = ns.Log4Moz.repository.rootLogger;
-  let appender = new ns.Log4Moz.DumpAppender();
-  appender.level = ns.Log4Moz.Level.All;
-  rootLogger.addAppender(appender);
+  rootLogger = ns.Log4Moz.repository.rootLogger;
+  dumpAppender = new ns.Log4Moz.DumpAppender();
+  dumpAppender.level = ns.Log4Moz.Level.All;
+  rootLogger.addAppender(dumpAppender);
 
   let notification = document.getElementById("global-notificationbox");
   let policy;
 
   notification.addEventListener("AlertActive", function active() {
     notification.removeEventListener("AlertActive", active, true);
 
     executeSoon(function afterNotification() {
@@ -121,16 +123,19 @@ function test_multiple_windows() {
         }
 
         if (!childWindowClosed) {
           dump("Not finishing test yet because child window isn't closed.\n");
           return;
         }
 
         dump("Finishing multiple window test.\n");
+        rootLogger.removeAppender(dumpAppender);
+        delete dumpAppender;
+        delete rootLogger;
         finish();
       }
 
       let closeCount = 0;
       function onAlertClose() {
         closeCount++;
 
         if (closeCount != 2) {
--- a/configure.in
+++ b/configure.in
@@ -78,17 +78,17 @@ MSMANIFEST_TOOL=
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
 
 dnl Initialize the Pthread test variables early so they can be
 dnl  overridden by each platform.
 dnl ========================================================
-USE_PTHREADS=
+MOZ_USE_PTHREADS=
 _PTHREAD_LDFLAGS=""
 
 dnl Do not allow a separate objdir build if a srcdir build exists.
 dnl ==============================================================
 _topsrcdir=`cd \`dirname $0\`; pwd`
 _objdir=`pwd`
 
 if test "$_topsrcdir" != "$_objdir"
@@ -3151,50 +3151,50 @@ AC_SUBST(XT_LIBS)
 AC_SUBST(XSS_LIBS)
 
 dnl ========================================================
 dnl = pthread support
 dnl = Start by checking whether the system support pthreads
 dnl ========================================================
 case "$target_os" in
 darwin*)
-    USE_PTHREADS=1
+    MOZ_USE_PTHREADS=1
     ;;
 *)
     MOZ_CHECK_PTHREADS(pthreads,
-        USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthreads",
+        MOZ_USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthreads",
         MOZ_CHECK_PTHREADS(pthread,
-            USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthread",
+            MOZ_USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lpthread",
             MOZ_CHECK_PTHREADS(c_r,
-                USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lc_r",
+                MOZ_USE_PTHREADS=1 _PTHREAD_LDFLAGS="-lc_r",
                 MOZ_CHECK_PTHREADS(c,
-                    USE_PTHREADS=1
+                    MOZ_USE_PTHREADS=1
                 )
             )
         )
     )
     ;;
 esac
 
 dnl ========================================================
 dnl Check the command line for --with-pthreads
 dnl ========================================================
 MOZ_ARG_WITH_BOOL(pthreads,
 [  --with-pthreads         Force use of system pthread library with NSPR ],
-[ if test "$USE_PTHREADS"x = x; then
+[ if test "$MOZ_USE_PTHREADS"x = x; then
     AC_MSG_ERROR([ --with-pthreads specified for a system without pthread support ]);
 fi],
-    USE_PTHREADS=
+    MOZ_USE_PTHREADS=
     _PTHREAD_LDFLAGS=
 )
 
 dnl ========================================================
 dnl Do the platform specific pthread hackery
 dnl ========================================================
-if test "$USE_PTHREADS"x != x
+if test "$MOZ_USE_PTHREADS"x != x
 then
 	dnl
 	dnl See if -pthread is supported.
 	dnl
 	rm -f conftest*
 	ac_cv_have_dash_pthread=no
 	AC_MSG_CHECKING(whether ${CC-cc} accepts -pthread)
 	echo 'int main() { return 0; }' | cat > conftest.c
@@ -3270,16 +3270,17 @@ then
 			AC_DEFINE(_REENTRANT)
 			if test "$SOLARIS_SUNPRO_CC"; then
 				CFLAGS="$CFLAGS -mt"
 				CXXFLAGS="$CXXFLAGS -mt"
 			fi
 			;;
 	esac
     LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}"
+    AC_SUBST(MOZ_USE_PTHREADS)
 fi
 
 
 dnl Checks for library functions.
 dnl ========================================================
 AC_PROG_GCC_TRADITIONAL
 AC_FUNC_MEMCMP
 AC_CHECK_FUNCS(random strerror lchown fchmod snprintf memmove rint stat64 lstat64 truncate64 setbuf isatty)
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -2001,16 +2001,17 @@ GK_ATOM(touch_enabled, "touch-enabled")
 GK_ATOM(maemo_classic, "maemo-classic")
 GK_ATOM(menubar_drag, "menubar-drag")
 GK_ATOM(swipe_animation_enabled, "swipe-animation-enabled")
 GK_ATOM(physical_home_button, "physical-home-button")
 
 // windows theme selector metrics
 GK_ATOM(windows_classic, "windows-classic")
 GK_ATOM(windows_theme_aero, "windows-theme-aero")
+GK_ATOM(windows_theme_aero_lite, "windows-theme-aero-lite")
 GK_ATOM(windows_theme_luna_blue, "windows-theme-luna-blue")
 GK_ATOM(windows_theme_luna_olive, "windows-theme-luna-olive")
 GK_ATOM(windows_theme_luna_silver, "windows-theme-luna-silver")
 GK_ATOM(windows_theme_royale, "windows-theme-royale")
 GK_ATOM(windows_theme_zune, "windows-theme-zune")
 GK_ATOM(windows_theme_generic, "windows-theme-generic")
 
 // And the same again, as media query keywords.
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -17,16 +17,17 @@
 #include "nsAttrValueInlines.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
+#include "nsIDocShell.h"
 #include "nsError.h"
 #include "nsNodeInfoManager.h"
 #include "nsNetUtil.h"
 #include "nsXPCOMStrings.h"
 #include "xpcpublic.h"
 #include "nsThreadUtils.h"
 #include "nsIThreadInternal.h"
 #include "nsContentUtils.h"
@@ -1055,16 +1056,25 @@ nsresult HTMLMediaElement::LoadResource(
   NS_ASSERTION(mDelayingLoadEvent,
                "Should delay load event (if in document) during load");
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannel = nullptr;
   }
 
+  // Check if media is allowed for the docshell.
+  nsCOMPtr<nsISupports> container = OwnerDoc()->GetContainer();
+  if (container) {
+    nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
+    if (docShell && !docShell->GetAllowMedia()) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
                                           mLoadingSrc,
                                           NodePrincipal(),
                                           static_cast<Element*>(this),
                                           EmptyCString(), // mime type
                                           nullptr, // extra
                                           &shouldLoad,
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -352,16 +352,21 @@ MOCHITEST_FILES = \
 		test_bug839371.html \
 		test_element_prototype.html \
 		test_formData.html \
 		test_audio_wakelock.html \
 		test_video_wakelock.html \
 		wakelock.ogg \
 		wakelock.ogv \
 		test_bug869040.html \
+		allowMedia.sjs \
+		$(NULL)
+
+MOCHITEST_CHROME_FILES = \
+		test_allowMedia.html \
 		$(NULL)
 
 MOCHITEST_BROWSER_FILES = \
 		browser_bug649778.js \
 		file_bug649778.html \
 		file_bug649778.html^headers^ \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/allowMedia.sjs
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(req, resp) {
+  resp.setHeader("Cache-Control", "no-cache", false);
+  resp.setHeader("Content-Type", "text/plain", false);
+
+  let stateKey = "allowMediaState";
+  let state = getState(stateKey);
+  setState(stateKey, req.queryString ? "FAIL" : "");
+  resp.write(state || "PASS");
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_allowMedia.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759964
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 759964</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 759964 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runNextTest);
+
+var SJS = "http://mochi.test:8888/tests/content/html/content/test/allowMedia.sjs";
+var TEST_PAGE = "data:text/html,<audio src='" + SJS + "?audio'></audio>";
+
+var Ci = Components.interfaces;
+
+function runNextTest() {
+  var test = tests.shift();
+  if (!test) {
+    SimpleTest.finish();
+    return;
+  }
+  test();
+}
+
+var tests = [
+
+  // Set allowMedia = false, load a page with <audio>, verify the <audio>
+  // doesn't load its source.
+  function basic() {
+    var iframe = insertIframe();
+    docshellForWindow(iframe.contentWindow).allowMedia = false;
+    loadIframe(iframe, TEST_PAGE, function () {
+      verifyPass();
+      iframe.remove();
+      runNextTest();
+    });
+  },
+
+  // Set allowMedia = false on parent docshell, load a page with <audio> in a
+  // child iframe, verify the <audio> doesn't load its source.
+  function inherit() {
+    var docshell = docshellForWindow(window);
+    docshell.allowMedia = false;
+
+    var iframe = insertIframe();
+    loadIframe(iframe, TEST_PAGE, function () {
+      verifyPass();
+      iframe.remove();
+      docshell.allowMedia = true;
+      runNextTest();
+    });
+  },
+
+  // In a display:none iframe, set allowMedia = false, load a page with <audio>,
+  // verify the <audio> doesn't load its source.
+  function displayNone() {
+    var iframe = insertIframe();
+    iframe.style.display = "none";
+    docshellForWindow(iframe.contentWindow).allowMedia = false;
+    loadIframe(iframe, TEST_PAGE, function () {
+      verifyPass();
+      iframe.remove();
+      runNextTest();
+    });
+  },
+];
+
+function docshellForWindow(win) {
+  return win.
+         QueryInterface(Ci.nsIInterfaceRequestor).
+         getInterface(Ci.nsIWebNavigation).
+         QueryInterface(Ci.nsIDocShell);
+}
+
+function insertIframe() {
+  var iframe = document.createElement("iframe");
+  document.body.appendChild(iframe);
+  return iframe;
+}
+
+function loadIframe(iframe, url, onDone) {
+  iframe.setAttribute("src", url);
+  iframe.addEventListener("load", onDone);
+}
+
+function verifyPass() {
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", SJS, false);
+  xhr.send();
+  is(xhr.responseText, "PASS", "<audio> source should not have been loaded.");
+}
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=759964">Mozilla Bug 759964</a>
+<p id="display">
+</p>
+</body>
+</html>
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -489,16 +489,35 @@ var gMetadataTests = [
       name:"歌曲名稱",
       comments:"註解",
     }
   },
   { name:"wavedata_u8.wav", tags: { }
   },
 ];
 
+// The resources for test_too_many_elements.
+// We loads *lots* of these all at the same time.
+var gTooManyElementAudioTests = [
+  { name:"small-shot.ogg", type:"audio/ogg" },
+  { name:"r11025_s16_c1.wav", type:"audio/x-wav" },
+  { name:"detodos.opus", type:"audio/ogg; codecs=opus" },
+  { name:"bogus.duh", type:"bogus/duh" }
+];
+// Unfortunately the WMF backend on Windows 7 can't handle having lots of
+// decoders running concurrently, so we only include MPEG files when running on
+// Windows versions 8 and later, and non Windows systems.
+if (navigator.userAgent.indexOf("Windows") == -1 ||
+    IsWindows8OrLater()) {
+  gTooManyElementAudioTests = gTooManyElementAudioTests.concat([
+    { name:"small-shot.m4a", type:"audio/mp4" },
+    { name:"small-shot.mp3", type:"audio/mpeg" }
+  ]);
+}
+
 function checkMetadata(msg, e, test) {
   if (test.width) {
     is(e.videoWidth, test.width, msg + " video width");
   }
   if (test.height) {
     is(e.videoHeight, test.height, msg + " video height");
   }
   if (test.duration) {
--- a/content/media/test/test_too_many_elements.html
+++ b/content/media/test/test_too_many_elements.html
@@ -19,50 +19,72 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p>Received loadeddata event for <span id="result">0</span> <span id="expected"></span>audio elements.</p>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 713381 **/
 
 const num = 500;
 var loadeddata = 0;
+var testName = "?";
 
 var result = document.getElementById("result");
 document.getElementById("expected").innerHTML = " of " + num + " ";
 
 var finish = function(testNum) {
   return function() {
-    ok(true, "Received loadeddata event for instance " + testNum );
+    ok(true, testName + ": received loadeddata event for instance " + testNum );
     loadeddata++;
     if (loadeddata == num) {
-      ok(true, "Should receive loadeddata events for all " + num + " elements.");
-      SimpleTest.finish();
+      ok(true, testName + ": should receive loadeddata events for all " + num + " elements.");
+      nextTest();
     }
   }
 };
 
-var resource = getPlayableAudio(gSmallTests);
+var testNum = 0;
+
+function nextTest() {
+  if (testNum >= gTooManyElementAudioTests.length) {
+    SimpleTest.finish();
+    return;
+  }
+  var test = gTooManyElementAudioTests[testNum];
+  testNum++;
 
-if (resource == null) {
-  todo(false, "No types supported");
-} else {
-  SimpleTest.waitForExplicitFinish();
+  if (!document.createElement("audio").canPlayType(test.type)) {
+    setTimeout(nextTest, 0);
+    return;
+  }
+  var name = test.name;
+  
+  loadeddata = 0;
+  testName = name;
+
   // Load the resource, and play it to ensure it's entirely downloaded.
   // Once it's played through, create a large number of audio elements which
   // are the same resource. These will share data with the other resource, and
   // so be really cheap to create.
-  var res = new Audio(resource.name);
+  var res = new Audio(name);
   res.addEventListener("ended",
     function() {  
       for (var i=0; i<num; ++i) {
-        var a = new Audio(resource.name);
+        var a = new Audio(name);
         a.addEventListener("loadeddata", finish(i), false);
       }
     }, false);
   res.play();
+  
 }
 
-setInterval(function() { result.innerHTML = loadeddata; }, 1000);
+if (getPlayableAudio(gTooManyElementAudioTests) == null) {
+  todo(false, "No types supported");
+} else {
+  SimpleTest.waitForExplicitFinish();
+  nextTest();
+}
+
+setInterval(function() { result.innerHTML = testName + ": " + loadeddata; }, 1000);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/wmf/DXVA2Manager.cpp
+++ b/content/media/wmf/DXVA2Manager.cpp
@@ -163,37 +163,51 @@ D3D9DXVA2Manager::CopyToImage(IMFSample*
   D3D9SurfaceImage* videoImage = static_cast<D3D9SurfaceImage*>(image.get());
   hr = videoImage->SetData(D3D9SurfaceImage::Data(surface, aRegion));
 
   image.forget(aOutImage);
 
   return S_OK;
 }
 
+// Count of the number of DXVAManager's we've created. This is also the
+// number of videos we're decoding with DXVA. Use on main thread only.
+static uint32_t sDXVAVideosCount = 0;
+
 /* static */
 DXVA2Manager*
 DXVA2Manager::Create()
 {
   MOZ_ASSERT(NS_IsMainThread());
   HRESULT hr;
 
+  // DXVA processing takes up a lot of GPU resources, so limit the number of
+  // videos we use DXVA with at any one time.
+  const uint32_t dxvaLimit =
+    Preferences::GetInt("media.windows-media-foundation.max-dxva-videos", 8);
+  if (sDXVAVideosCount == dxvaLimit) {
+    return nullptr;
+  }
+
   nsAutoPtr<D3D9DXVA2Manager> d3d9Manager(new D3D9DXVA2Manager());
   hr = d3d9Manager->Init();
   if (SUCCEEDED(hr)) {
     return d3d9Manager.forget();
   }
 
   // No hardware accelerated video decoding. :(
   return nullptr;
 }
 
 DXVA2Manager::DXVA2Manager()
   : mLock("DXVA2Manager")
 {
   MOZ_ASSERT(NS_IsMainThread());
+  ++sDXVAVideosCount;
 }
 
 DXVA2Manager::~DXVA2Manager()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  --sDXVAVideosCount;
 }
 
 } // namespace mozilla
--- a/content/media/wmf/WMFByteStream.cpp
+++ b/content/media/wmf/WMFByteStream.cpp
@@ -23,16 +23,19 @@ namespace mozilla {
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gWMFByteStreamLog = nullptr;
 #define LOG(...) PR_LOG(gWMFByteStreamLog, PR_LOG_DEBUG, (__VA_ARGS__))
 #else
 #define LOG(...)
 #endif
 
+// Limit the number of threads that we use for IO.
+static const uint32_t NumWMFIoThreads = 4;
+
 // Thread pool listener which ensures that MSCOM is initialized and
 // deinitialized on the thread pool thread. We can call back into WMF
 // on this thread, so we need MSCOM working.
 class ThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITHREADPOOLLISTENER
 };
@@ -138,16 +141,29 @@ WMFByteStream::Init()
     nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     sThreadPool = pool;
     NS_ADDREF(sThreadPool);
 
     rv = sThreadPool->SetName(NS_LITERAL_CSTRING("WMFByteStream Async Read Pool"));
     NS_ENSURE_SUCCESS(rv, rv);
+    
+    // We limit the number of threads that we use for IO. Note that the thread
+    // limit is the same as the idle limit so that we're not constantly creating
+    // and destroying threads. When the thread pool threads shutdown they
+    // dispatch an event to the main thread to call nsIThread::Shutdown(),
+    // and if we're very busy that can take a while to run, and we end up with
+    // dozens of extra threads. Note that threads that are idle for 60 seconds
+    // are shutdown naturally.
+    rv = sThreadPool->SetThreadLimit(NumWMFIoThreads);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = sThreadPool->SetIdleThreadLimit(NumWMFIoThreads);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIThreadPoolListener> listener = new ThreadPoolListener();
     rv = sThreadPool->SetListener(listener);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   sThreadPoolRefCnt++;
 
   // Store a ref to the thread pool, so that we keep the pool alive as long as
--- a/content/media/wmf/WMFReader.cpp
+++ b/content/media/wmf/WMFReader.cpp
@@ -115,19 +115,28 @@ WMFReader::InitializeDXVA()
   NS_ENSURE_TRUE(layerManager, false);
 
   if (layerManager->GetBackendType() != LayersBackend::LAYERS_D3D9 &&
       layerManager->GetBackendType() != LayersBackend::LAYERS_D3D10) {
     return false;
   }
 
   mDXVA2Manager = DXVA2Manager::Create();
-  NS_ENSURE_TRUE(mDXVA2Manager, false);
+
+  return mDXVA2Manager != nullptr;
+}
 
-  return true;
+static bool
+IsVideoContentType(const nsCString& aContentType)
+{
+  NS_NAMED_LITERAL_CSTRING(video, "video");
+  if (FindInReadable(video, aContentType)) {
+    return true;
+  }
+  return false;
 }
 
 nsresult
 WMFReader::Init(MediaDecoderReader* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
 
   nsresult rv = WMFDecoder::LoadDLLs();
@@ -140,17 +149,21 @@ WMFReader::Init(MediaDecoderReader* aClo
 
   mSourceReaderCallback = new WMFSourceReaderCallback();
 
   // Must be created on main thread.
   mByteStream = new WMFByteStream(mDecoder->GetResource(), mSourceReaderCallback);
   rv = mByteStream->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mUseHwAccel = InitializeDXVA();
+  if (IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
+    mUseHwAccel = InitializeDXVA();
+  } else {
+    mUseHwAccel = false;
+  }
 
   return NS_OK;
 }
 
 bool
 WMFReader::HasAudio()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -748,16 +748,17 @@ nsDocShell::nsDocShell():
     mSandboxFlags(0),
     mFullscreenAllowed(CHECK_ATTRIBUTES),
     mCreated(false),
     mAllowSubframes(true),
     mAllowPlugins(true),
     mAllowJavascript(true),
     mAllowMetaRedirects(true),
     mAllowImages(true),
+    mAllowMedia(true),
     mAllowDNSPrefetch(true),
     mAllowWindowControl(true),
     mCreatingDocument(false),
     mUseErrorPages(false),
     mObserveErrorPages(true),
     mAllowAuth(true),
     mAllowKeywordFixup(false),
     mIsOffScreenBrowser(false),
@@ -2278,16 +2279,28 @@ NS_IMETHODIMP nsDocShell::GetAllowImages
 }
 
 NS_IMETHODIMP nsDocShell::SetAllowImages(bool aAllowImages)
 {
     mAllowImages = aAllowImages;
     return NS_OK;
 }
 
+NS_IMETHODIMP nsDocShell::GetAllowMedia(bool * aAllowMedia)
+{
+    *aAllowMedia = mAllowMedia;
+    return NS_OK;
+}
+
+NS_IMETHODIMP nsDocShell::SetAllowMedia(bool aAllowMedia)
+{
+    mAllowMedia = aAllowMedia;
+    return NS_OK;
+}
+
 NS_IMETHODIMP nsDocShell::GetAllowDNSPrefetch(bool * aAllowDNSPrefetch)
 {
     *aAllowDNSPrefetch = mAllowDNSPrefetch;
     return NS_OK;
 }
 
 NS_IMETHODIMP nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch)
 {
@@ -2820,16 +2833,17 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
         if (NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value)))
         {
             SetAllowSubframes(value);
         }
         if (NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value)))
         {
             SetAllowImages(value);
         }
+        SetAllowMedia(parentAsDocShell->GetAllowMedia());
         if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value)))
         {
             SetAllowWindowControl(value);
         }
         if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value)))
         {
             SetIsActive(value);
         }
@@ -7723,30 +7737,33 @@ nsDocShell::RestoreFromHistory()
         childShell->GetAllowMetaRedirects(&allowRedirects);
 
         bool allowSubframes;
         childShell->GetAllowSubframes(&allowSubframes);
 
         bool allowImages;
         childShell->GetAllowImages(&allowImages);
 
+        bool allowMedia = childShell->GetAllowMedia();
+
         bool allowDNSPrefetch;
         childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
 
         // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning
         // that the child inherits our state. Among other things, this means
         // that the child inherits our mIsActive and mInPrivateBrowsing, which
         // is what we want.
         AddChild(childItem);
 
         childShell->SetAllowPlugins(allowPlugins);
         childShell->SetAllowJavascript(allowJavascript);
         childShell->SetAllowMetaRedirects(allowRedirects);
         childShell->SetAllowSubframes(allowSubframes);
         childShell->SetAllowImages(allowImages);
+        childShell->SetAllowMedia(allowMedia);
         childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
 
         rv = childShell->BeginRestore(nullptr, false);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsCOMPtr<nsIPresShell> shell = GetPresShell();
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -798,16 +798,17 @@ protected:
     FullscreenAllowedState     mFullscreenAllowed;
 
     bool                       mCreated;
     bool                       mAllowSubframes;
     bool                       mAllowPlugins;
     bool                       mAllowJavascript;
     bool                       mAllowMetaRedirects;
     bool                       mAllowImages;
+    bool                       mAllowMedia;
     bool                       mAllowDNSPrefetch;
     bool                       mAllowWindowControl;
     bool                       mCreatingDocument; // (should be) debugging only
     bool                       mUseErrorPages;
     bool                       mObserveErrorPages;
     bool                       mAllowAuth;
     bool                       mAllowKeywordFixup;
     bool                       mIsOffScreenBrowser;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -37,17 +37,17 @@ interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 
-[scriptable, builtinclass, uuid(bb1a1c98-4deb-44ea-b607-aa7a1ad8abae)]
+[scriptable, builtinclass, uuid(f453d2ee-bac7-46f9-a553-df918f0cc0d0)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -230,16 +230,21 @@ interface nsIDocShell : nsIDocShellTreeI
   attribute boolean allowSubframes;
 
   /**
    * Attribute stating whether or not images should be loaded.
    */
   attribute boolean allowImages;
 
   /**
+   * Attribute stating whether or not media (audio/video) should be loaded.
+   */
+  [infallible] attribute boolean allowMedia;
+
+  /**
    * Attribute that determines whether DNS prefetch is allowed for this subtree
    * of the docshell tree.  Defaults to true.  Setting this will make it take
    * effect starting with the next document loaded in the docshell.
    */
   attribute boolean allowDNSPrefetch;
 
   /**
    * Attribute that determines whether window control (move/resize) is allowed.
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3282,16 +3282,46 @@ nsDOMWindowUtils::AllowScriptsToClose()
   }
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
   static_cast<nsGlobalWindow*>(window.get())->AllowScriptsToClose();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetIsParentWindowMainWidgetVisible(bool* aIsVisible)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  // this should reflect the "is parent window visible" logic in
+  // nsWindowWatcher::OpenWindowInternal()
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+
+  nsCOMPtr<nsIWidget> parentWidget;
+  nsIDocShell *docShell = window->GetDocShell();
+  if (docShell) {
+    nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
+    docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
+    nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
+    if (parentWindow) {
+        parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
+    }
+  }
+  if (!parentWidget) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  *aIsVisible = parentWidget->IsVisible();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::IsNodeDisabledForEvents(nsIDOMNode* aNode, bool* aRetVal)
 {
   *aRetVal = false;
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
   nsCOMPtr<nsINode> n = do_QueryInterface(aNode);
   nsINode* node = n;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -37,17 +37,17 @@ interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 
-[scriptable, uuid(a806d366-cc52-11e2-bc9a-ba3212c84021)]
+[scriptable, uuid(cbe333d7-5b2c-4a9b-b99b-e6e388afa62b)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1361,16 +1361,27 @@ interface nsIDOMWindowUtils : nsISupport
   readonly attribute boolean isHandlingUserInput;
 
   /**
    * After calling the method, the window for which this DOMWindowUtils
    * was created can be closed using scripts.
    */
    void allowScriptsToClose();
 
+  /**
+   * Is the parent window's main widget visible?  If it isn't, we probably
+   * don't want to display any dialogs etc it may request.  This corresponds
+   * to the visibility check in nsWindowWatcher::OpenWindowInternal().
+   *
+   * Will throw a DOM security error if called without chrome privileges or
+   * NS_ERROR_NOT_AVAILABLE in the unlikely event that the parent window's
+   * main widget can't be reached.
+   */
+  readonly attribute boolean isParentWindowMainWidgetVisible;
+
    /**
     * In certain cases the event handling of nodes, form controls in practice,
     * may be disabled. Such cases are for example the existence of disabled
     * attribute or -moz-user-input: none/disabled.
     */
    boolean isNodeDisabledForEvents(in nsIDOMNode aNode);
 
    /**
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1467,17 +1467,20 @@ bool
 TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
 
     uint32_t presShellId;
     nsresult rv = utils->GetPresShellId(&presShellId);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     if (NS_SUCCEEDED(rv) && aFrameMetrics.mPresShellId != presShellId) {
-        return true;
+        // We've recieved a message that is out of date and we want to ignore.
+        // However we can't reply without painting so we reply by painting the
+        // exact same thing as we did before.
+        return ProcessUpdateFrame(mLastMetrics);
     }
     return ProcessUpdateFrame(aFrameMetrics);
 }
 
 bool
 TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
   {
     if (!mCx || !mTabChildGlobal) {
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -117,17 +117,17 @@ function runTest(aCallback) {
   SpecialPowers.pushPrefEnv({'set': [
     ['media.peerconnection.enabled', true],
     ['media.navigator.permission.disabled', true]]
   }, function () {
     try {
       aCallback();
     }
     catch (err) {
-      unexpectedCallbackAndFinish(new Error)(err);
+      unexpectedCallbackAndFinish()(err);
     }
   });
 }
 
 /**
  * Checks that the media stream tracks have the expected amount of tracks
  * with the correct kind and id based on the type and constraints given.
  *
@@ -165,40 +165,50 @@ function checkMediaStreamTracks(constrai
     mediaStream.getVideoTracks());
 }
 
 /**
  * Generates a callback function fired only under unexpected circumstances
  * while running the tests. The generated function kills off the test as well
  * gracefully.
  *
- * @param {Error} error
- *        A new Error object, generated at the callback site, from which a
- *        filename and line number can be extracted for diagnostic purposes
+ * @param {String} [message]
+ *        An optional message to show if no object gets passed into the
+ *        generated callback method.
  */
-function unexpectedCallbackAndFinish(error) {
+function unexpectedCallbackAndFinish(message) {
+  var stack = new Error().stack.split("\n");
+  stack.shift(); // Don't include this instantiation frame
+
   /**
    * @param {object} aObj
    *        The object fired back from the callback
    */
-  return function(aObj) {
-    var where = error.fileName + ":" + error.lineNumber;
+  return function (aObj) {
     if (aObj && aObj.name && aObj.message) {
-      ok(false, "Unexpected callback/event from " + where + " with name = '" +
-                aObj.name + "', message = '" + aObj.message + "'");
+      ok(false, "Unexpected callback for '" + aObj.name + "' with message = '" +
+         aObj.message + "' at " + JSON.stringify(stack));
     } else {
-      ok(false, "Unexpected callback/event from " + where + " with " + aObj);
+      ok(false, "Unexpected callback with message = '" + message +
+         "' at: " + JSON.stringify(stack));
     }
     SimpleTest.finish();
   }
 }
 
 /**
- * Generates a callback function suitable for putting int a success
- * callback in circumstances where success is unexpected. The callback,
- * if activated, will kill off the test gracefully.
+ * Generates a callback function fired only for unexpected events happening.
+ *
+ * @param {String} description
+          Description of the object for which the event has been fired
+ * @param {String} eventName
+          Name of the unexpected event
  */
+function unexpectedEventAndFinish(message, eventName) {
+  var stack = new Error().stack.split("\n");
+  stack.shift(); // Don't include this instantiation frame
 
-function unexpectedSuccessCallbackAndFinish(error, reason) {
-  return function() {
-    unexpectedCallbackAndFinish(error)(message);
+  return function () {
+    ok(false, "Unexpected event '" + eventName + "' fired with message = '" +
+       message + "' at: " + JSON.stringify(stack));
+    SimpleTest.finish();
   }
 }
--- a/dom/media/tests/mochitest/mediaStreamPlayback.js
+++ b/dom/media/tests/mochitest/mediaStreamPlayback.js
@@ -116,18 +116,17 @@ MediaStreamPlayback.prototype = {
       self.mediaElement.addEventListener('timeupdate', timeUpdateCallback,
         false);
 
       // If timeupdate doesn't fire in enough time, we fail the test
       setTimeout(function() {
         if (!timeUpdateFired) {
           self.mediaElement.removeEventListener('timeupdate',
             timeUpdateCallback, false);
-          ok(false, "timeUpdate event never fired");
-          onError();
+          onError("timeUpdate event never fired");
         }
       }, TIMEOUT_LENGTH);
     };
 
     // Adds a listener intended to be fired when playback is available
     // without further buffering.
     this.mediaElement.addEventListener('canplaythrough', canPlayThroughCallback,
       false);
@@ -136,18 +135,17 @@ MediaStreamPlayback.prototype = {
     this.mediaElement.mozSrcObject = this.mediaStream;
     this.mediaElement.play();
 
     // If canplaythrough doesn't fire in enough time, we fail the test
     setTimeout(function() {
       if (!canPlayThroughFired) {
         self.mediaElement.removeEventListener('canplaythrough',
           canPlayThroughCallback, false);
-        ok(false, "canplaythrough event never fired");
-        onError();
+        onError("canplaythrough event never fired");
       }
     }, TIMEOUT_LENGTH);
   },
 
   /**
    * Stops the media with the associated stream.
    *
    * Precondition: The media stream and element should both be actively
@@ -232,15 +230,14 @@ LocalMediaStreamPlayback.prototype = Obj
       };
 
       this.mediaElement.addEventListener('ended', endedCallback, false);
       this.mediaStream.stop();
 
       // If ended doesn't fire in enough time, then we fail the test
       setTimeout(function() {
         if (!endedFired) {
-          ok(false, "ended event never fired");
-          onError();
+          onError("ended event never fired");
         }
       }, TIMEOUT_LENGTH);
     }
   }
 });
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -244,69 +244,81 @@ var commandsPeerConnection = [
       test.pcRemote.getAllUserMedia(function () {
         test.next();
       });
     }
   ],
   [
     'PC_CHECK_INITIAL_SIGNALINGSTATE',
     function (test) {
-      is(test.pcLocal.signalingState,"stable", "Initial local signalingState is stable");
-      is(test.pcRemote.signalingState,"stable", "Initial remote signalingState is stable");
+      is(test.pcLocal.signalingState, "stable",
+         "Initial local signalingState is 'stable'");
+      is(test.pcRemote.signalingState, "stable",
+         "Initial remote signalingState is 'stable'");
       test.next();
     }
   ],
   [
     'PC_LOCAL_CREATE_OFFER',
     function (test) {
-      test.pcLocal.createOffer(function () {
-        is(test.pcLocal.signalingState, "stable", "Local create offer does not change signaling state");
+      test.createOffer(test.pcLocal, function () {
+        is(test.pcLocal.signalingState, "stable",
+           "Local create offer does not change signaling state");
         test.next();
       });
     }
   ],
   [
     'PC_LOCAL_SET_LOCAL_DESCRIPTION',
     function (test) {
-      test.expectStateChange(test.pcLocal, "have-local-offer", test);
-      test.pcLocal.setLocalDescription(test.pcLocal._last_offer,
-        test.checkStateInCallback(test.pcLocal, "have-local-offer", test));
+      test.setLocalDescription(test.pcLocal, test.pcLocal._last_offer, function () {
+        is(test.pcLocal.signalingState, "have-local-offer",
+           "signalingState after local setLocalDescription is 'have-local-offer'");
+        test.next();
+      });
     }
   ],
   [
     'PC_REMOTE_SET_REMOTE_DESCRIPTION',
     function (test) {
-      test.expectStateChange(test.pcRemote, "have-remote-offer", test);
-      test.pcRemote.setRemoteDescription(test.pcLocal._last_offer,
-        test.checkStateInCallback(test.pcRemote, "have-remote-offer", test));
+      test.setRemoteDescription(test.pcRemote, test.pcLocal._last_offer, function () {
+        is(test.pcRemote.signalingState, "have-remote-offer",
+           "signalingState after remote setRemoteDescription is 'have-remote-offer'");
+        test.next();
+      });
     }
   ],
   [
     'PC_REMOTE_CREATE_ANSWER',
     function (test) {
-      test.pcRemote.createAnswer(function () {
-        is(test.pcRemote.signalingState, "have-remote-offer", "Remote create offer does not change signaling state");
+      test.createAnswer(test.pcRemote, function () {
+        is(test.pcRemote.signalingState, "have-remote-offer",
+           "Remote createAnswer does not change signaling state");
         test.next();
       });
     }
   ],
   [
     'PC_LOCAL_SET_REMOTE_DESCRIPTION',
     function (test) {
-      test.expectStateChange(test.pcLocal, "stable", test);
-      test.pcLocal.setRemoteDescription(test.pcRemote._last_answer,
-        test.checkStateInCallback(test.pcLocal, "stable", test));
+      test.setRemoteDescription(test.pcLocal, test.pcRemote._last_answer, function () {
+        is(test.pcLocal.signalingState, "stable",
+           "signalingState after local setRemoteDescription is 'stable'");
+        test.next();
+      });
     }
   ],
   [
     'PC_REMOTE_SET_LOCAL_DESCRIPTION',
     function (test) {
-      test.expectStateChange(test.pcRemote, "stable", test);
-      test.pcRemote.setLocalDescription(test.pcRemote._last_answer,
-        test.checkStateInCallback(test.pcRemote, "stable", test));
+      test.setLocalDescription(test.pcRemote, test.pcRemote._last_answer, function () {
+        is(test.pcRemote.signalingState, "stable",
+           "signalingState after remote setLocalDescription is 'stable'");
+        test.next();
+      });
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA',
     function (test) {
       test.pcLocal.checkMedia(test.pcRemote.constraints);
       test.next();
     }
@@ -352,16 +364,83 @@ function PeerConnectionTest(options) {
 /**
  * Executes the next command.
  */
 PeerConnectionTest.prototype.next = function PCT_next() {
   this.chain.executeNext();
 };
 
 /**
+ * Creates an answer for the specified peer connection instance
+ * and automatically handles the failure case.
+ *
+ * @param {PeerConnectionWrapper} peer
+ *        The peer connection wrapper to run the command on
+ * @param {function} onSuccess
+ *        Callback to execute if the offer was created successfully
+ */
+PeerConnectionTest.prototype.createAnswer =
+function PCT_createAnswer(peer, onSuccess) {
+  peer.createAnswer(function (answer) {
+    onSuccess(answer);
+  });
+};
+
+/**
+ * Creates an offer for the specified peer connection instance
+ * and automatically handles the failure case.
+ *
+ * @param {PeerConnectionWrapper} peer
+ *        The peer connection wrapper to run the command on
+ * @param {function} onSuccess
+ *        Callback to execute if the offer was created successfully
+ */
+PeerConnectionTest.prototype.createOffer =
+function PCT_createOffer(peer, onSuccess) {
+  peer.createOffer(function (offer) {
+    onSuccess(offer);
+  });
+};
+
+/**
+ * Sets the local description for the specified peer connection instance
+ * and automatically handles the failure case.
+ *
+ * @param {PeerConnectionWrapper} peer
+          The peer connection wrapper to run the command on
+ * @param {mozRTCSessionDescription} desc
+ *        Session description for the local description request
+ * @param {function} onSuccess
+ *        Callback to execute if the local description was set successfully
+ */
+PeerConnectionTest.prototype.setLocalDescription =
+function PCT_setLocalDescription(peer, desc, onSuccess) {
+  var eventFired = false;
+  var stateChanged = false;
+
+  function check_next_test() {
+    if (eventFired && stateChanged) {
+      onSuccess();
+    }
+  }
+
+  peer.onsignalingstatechange = function () {
+    info(peer + ": 'onsignalingstatechange' event registered for async check");
+
+    eventFired = true;
+    check_next_test();
+  };
+
+  peer.setLocalDescription(desc, function () {
+    stateChanged = true;
+    check_next_test();
+  });
+};
+
+/**
  * Sets the media constraints for both peer connection instances.
  *
  * @param {object} constraintsLocal
  *        Media constrains for the local peer connection instance
  * @param constraintsRemote
  */
 PeerConnectionTest.prototype.setMediaConstraints =
 function PCT_setMediaConstraints(constraintsLocal, constraintsRemote) {
@@ -375,16 +454,51 @@ function PCT_setMediaConstraints(constra
  * @param {object} constraints the media constraints to use on createOffer
  */
 PeerConnectionTest.prototype.setOfferConstraints =
 function PCT_setOfferConstraints(constraints) {
   this.pcLocal.offerConstraints = constraints;
 };
 
 /**
+ * Sets the remote description for the specified peer connection instance
+ * and automatically handles the failure case.
+ *
+ * @param {PeerConnectionWrapper} peer
+          The peer connection wrapper to run the command on
+ * @param {mozRTCSessionDescription} desc
+ *        Session description for the remote description request
+ * @param {function} onSuccess
+ *        Callback to execute if the local description was set successfully
+ */
+PeerConnectionTest.prototype.setRemoteDescription =
+function PCT_setRemoteDescription(peer, desc, onSuccess) {
+  var eventFired = false;
+  var stateChanged = false;
+
+  function check_next_test() {
+    if (eventFired && stateChanged) {
+      onSuccess();
+    }
+  }
+
+  peer.onsignalingstatechange = function () {
+    info(peer + ": 'onsignalingstatechange' event registered for async check");
+
+    eventFired = true;
+    check_next_test();
+  };
+
+  peer.setRemoteDescription(desc, function () {
+    stateChanged = true;
+    check_next_test();
+  });
+};
+
+/**
  * Start running the tests as assigned to the command chain.
  */
 PeerConnectionTest.prototype.run = function PCT_run() {
   this.next();
 };
 
 /**
  * Clean up the objects used by the test
@@ -399,74 +513,16 @@ PeerConnectionTest.prototype.teardown = 
     this.pcRemote.close();
     this.pcRemote = null;
   }
 
   info("Test finished");
   SimpleTest.finish();
 };
 
-/**
- * Sets up the "onsignalingstatechange" handler for the indicated peerconnection
- * as a one-shot test. If the test.commandSuccess flag is set when the event
- * happens, then the next test in the command chain is triggered. After
- * running, this sets the event handler so that it will fail the test if
- * it fires again before we expect it. This is intended to be used in
- * conjunction with checkStateInCallback, below.
- *
- * @param {pcw} PeerConnectionWrapper
- *        The peer connection to expect a state change on
- * @param {state} string
- *        The state that we expect to change to
- * @param {test} PeerConnectionTest
- *        The test strucure currently in use.
- */
-PeerConnectionTest.prototype.expectStateChange =
-function PCT_expectStateChange(pcw, state, test) {
-  pcw.signalingChangeEvent = false;
-  pcw._pc.onsignalingstatechange = function() {
-    pcw._pc.onsignalingstatechange = unexpectedCallbackAndFinish(new Error);
-    is(pcw._pc.signalingState, state, pcw.label + ": State is " + state + " in onsignalingstatechange");
-    pcw.signalingChangeEvent = true;
-    if (pcw.commandSuccess) {
-      test.next();
-    } else {
-      info("Waiting for success callback...");
-    }
-  };
-}
-
-/**
- * Returns a function, suitable for use as a success callback, that
- * checks the signaling state of the PC; and, if the signalingstatechange
- * event has already fired, moves on to the next test case. This is
- * intended to be used in conjunction with expectStateChange, above.
- *
- * @param {pcw} PeerConnectionWrapper
- *        The peer connection to expect a state change on
- * @param {state} string
- *        The state that we expect to change to
- * @param {test} PeerConnectionTest
- *        The test strucure currently in use.
- */
-
-PeerConnectionTest.prototype.checkStateInCallback =
-function PCT_checkStateInCallback(pcw, state, test) {
-  pcw.commandSuccess = false;
-  return function() {
-    pcw.commandSuccess = true;
-    is(pcw.signalingState, state, pcw.label + ": State is " + state + " in success callback");
-    if (pcw.signalingChangeEvent) {
-      test.next();
-    } else {
-      info("Waiting for signalingstatechange event...");
-    }
-  };
-}
-
 
 /**
  * This class handles acts as a wrapper around a PeerConnection instance.
  *
  * @constructor
  * @param {string} label
  *        Description for the peer connection instance
  * @param {object} configuration
@@ -475,27 +531,46 @@ function PCT_checkStateInCallback(pcw, s
 function PeerConnectionWrapper(label, configuration) {
   this.configuration = configuration;
   this.label = label;
 
   this.constraints = [ ];
   this.offerConstraints = {};
   this.streams = [ ];
 
-  info("Creating new PeerConnectionWrapper: " + this.label);
+  info("Creating new PeerConnectionWrapper: " + this);
   this._pc = new mozRTCPeerConnection(this.configuration);
 
+  /**
+   * Setup callback handlers
+   */
+
+  this.onsignalingstatechange = unexpectedEventAndFinish(this, 'onsignalingstatechange');
+
+
   var self = this;
   this._pc.onaddstream = function (event) {
     // Bug 834835: Assume type is video until we get get{Audio,Video}Tracks.
     self.attachMedia(event.stream, 'video', 'remote');
   };
 
-  // Make sure no signaling state changes are fired until we expect them to
-  this._pc.onsignalingstatechange = unexpectedCallbackAndFinish(new Error);
+  /**
+   * Callback for native peer connection 'onsignalingstatechange' events. If no
+   * custom handler has been specified via 'this.onsignalingstatechange', a
+   * failure will be raised if an event of this type is caught.
+   *
+   * @param {Object} aEvent
+   *        Event data which includes the newly created data channel
+   */
+  this._pc.onsignalingstatechange = function (aEvent) {
+    info(self + ": 'onsignalingstatechange' event fired");
+
+    self.onsignalingstatechange();
+    self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange');
+  }
 }
 
 PeerConnectionWrapper.prototype = {
 
   /**
    * Returns the local description.
    *
    * @returns {object} The local description
@@ -588,17 +663,17 @@ PeerConnectionWrapper.prototype = {
 
           if (constraints.video) {
             type += 'video';
           }
 
           self.attachMedia(stream, type, 'local');
 
           _getAllUserMedia(constraintsList, index + 1);
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
       } else {
         onSuccess();
       }
     }
 
     info("Get " + this.constraints.length + " local streams");
     _getAllUserMedia(this.constraints, 0);
   },
@@ -611,163 +686,170 @@ PeerConnectionWrapper.prototype = {
    */
   createOffer : function PCW_createOffer(onSuccess) {
     var self = this;
 
     this._pc.createOffer(function (offer) {
       info("Got offer: " + JSON.stringify(offer));
       self._last_offer = offer;
       onSuccess(offer);
-    }, unexpectedCallbackAndFinish(new Error), this.offerConstraints);
+    }, unexpectedCallbackAndFinish(), this.offerConstraints);
   },
 
   /**
    * Creates an answer and automatically handles the failure case.
    *
    * @param {function} onSuccess
    *        Callback to execute if the answer was created successfully
    */
   createAnswer : function PCW_createAnswer(onSuccess) {
     var self = this;
 
     this._pc.createAnswer(function (answer) {
-      info('Got answer for ' + self.label + ': ' + JSON.stringify(answer));
+      info(self + ": Got answer: " + JSON.stringify(answer));
       self._last_answer = answer;
       onSuccess(answer);
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   },
 
   /**
    * Sets the local description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
    * @param {function} onSuccess
    *        Callback to execute if the local description was set successfully
    */
   setLocalDescription : function PCW_setLocalDescription(desc, onSuccess) {
     var self = this;
     this._pc.setLocalDescription(desc, function () {
-      info("Successfully set the local description for " + self.label);
+      info(self + ": Successfully set the local description");
       onSuccess();
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   },
 
   /**
    * Tries to set the local description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the local description request
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   setLocalDescriptionAndFail : function PCW_setLocalDescriptionAndFail(desc, onFailure) {
     var self = this;
     this._pc.setLocalDescription(desc,
-      unexpectedSuccessCallbackAndFinish(new Error, "setLocalDescription should have failed."),
+      unexpectedCallbackAndFinish("setLocalDescription should have failed."),
       function (err) {
-        info("As expected, failed to set the local description for " + self.label);
+        info(self + ": As expected, failed to set the local description");
         onFailure(err);
     });
   },
 
   /**
    * Sets the remote description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
    * @param {function} onSuccess
    *        Callback to execute if the remote description was set successfully
    */
   setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) {
     var self = this;
     this._pc.setRemoteDescription(desc, function () {
-      info("Successfully set remote description for " + self.label);
+      info(self + ": Successfully set remote description");
       onSuccess();
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   },
 
   /**
    * Tries to set the remote description and expect failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   setRemoteDescriptionAndFail : function PCW_setRemoteDescriptionAndFail(desc, onFailure) {
     var self = this;
     this._pc.setRemoteDescription(desc,
-      unexpectedSuccessCallbackAndFinish(new Error, "setRemoteDescription should have failed."),
+      unexpectedCallbackAndFinish("setRemoteDescription should have failed."),
       function (err) {
-        info("As expected, failed to set the remote description for " + self.label);
+        info(self + ": As expected, failed to set the remote description");
         onFailure(err);
     });
   },
 
   /**
    * Adds an ICE candidate and automatically handles the failure case.
    *
    * @param {object} candidate
    *        SDP candidate
    * @param {function} onSuccess
    *        Callback to execute if the local description was set successfully
    */
   addIceCandidate : function PCW_addIceCandidate(candidate, onSuccess) {
     var self = this;
 
     this._pc.addIceCandidate(candidate, function () {
-      info("Successfully added an ICE candidate to " + self.label);
+      info(self + ": Successfully added an ICE candidate");
       onSuccess();
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   },
 
   /**
    * Tries to add an ICE candidate and expects failure. Automatically
    * causes the test case to fail if the call succeeds.
    *
    * @param {object} candidate
    *        SDP candidate
    * @param {function} onFailure
    *        Callback to execute if the call fails.
    */
   addIceCandidateAndFail : function PCW_addIceCandidateAndFail(candidate, onFailure) {
     var self = this;
 
     this._pc.addIceCandidate(candidate,
-      unexpectedSuccessCallbackAndFinish(new Error, "addIceCandidate should have failed."),
+      unexpectedCallbackAndFinish("addIceCandidate should have failed."),
       function (err) {
-        info("As expected, failed to add an ICE candidate to " + self.label);
+        info(self + ": As expected, failed to add an ICE candidate");
         onFailure(err);
     }) ;
   },
 
   /**
    * Checks that we are getting the media we expect.
    *
    * @param {object} constraintsRemote
    *        The media constraints of the remote peer connection object
    */
   checkMedia : function PCW_checkMedia(constraintsRemote) {
     is(this._pc.localStreams.length, this.constraints.length,
-       this.label + ' has ' + this.constraints.length + ' local streams');
+       this + ' has ' + this.constraints.length + ' local streams');
 
     // TODO: change this when multiple incoming streams are allowed.
     is(this._pc.remoteStreams.length, 1,
-       this.label + ' has ' + 1 + ' remote streams');
+       this + ' has ' + 1 + ' remote streams');
   },
 
   /**
    * Closes the connection
    */
   close : function PCW_close() {
     // It might be that a test has already closed the pc. In those cases
     // we should not fail.
     try {
       this._pc.close();
-      info(this.label + ": Closed connection.");
+      info(this + ": Closed connection.");
     } catch (e) {
-      info(this.label + ": Failure in closing connection - " + e.message);
+      info(this + ": Failure in closing connection - " + e.message);
     }
+  },
+
+  /**
+   * Returns the string representation of the object
+   */
+  toString : function PCW_toString() {
+    return "PeerConnectionWrapper (" + this.label + ")";
   }
 };
--- a/dom/media/tests/mochitest/test_dataChannel_noOffer.html
+++ b/dom/media/tests/mochitest/test_dataChannel_noOffer.html
@@ -19,15 +19,15 @@
     // necessary to circumvent bug 864109
     var options = { mandatory: { OfferToReceiveAudio: true} };
 
     pc.createOffer(function (offer) {
       ok(!offer.sdp.contains("m=application"),
         "m=application is not contained in the SDP");
 
       SimpleTest.finish();
-    }, unexpectedCallbackAndFinish(new Error), options);
+    }, unexpectedCallbackAndFinish(), options);
   }, true);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio.html
@@ -29,18 +29,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testAudio, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideo.html
@@ -29,18 +29,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideo, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
 
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicVideoAudio.html
@@ -29,17 +29,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     getUserMedia(constraints, function (aStream) {
       checkMediaStreamTracks(constraints, aStream);
 
       var playback = new LocalMediaStreamPlayback(testVideoAudio, aStream);
       playback.playMedia(false, function () {
         aStream.stop();
         SimpleTest.finish();
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_gumWithinGum.html
@@ -37,21 +37,21 @@ https://bugzilla.mozilla.org/show_bug.cg
           var testAudio = document.getElementById('testAudio');
           var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
             audioStream);
 
           audioStreamPlayback.playMedia(false, function() {
             audioStream.stop();
             videoStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish(new Error));
+          }, unexpectedCallbackAndFinish());
 
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playAudioTwice.html
@@ -29,19 +29,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio,
         audioStream);
 
       audioStreamPlayback.playMedia(false, function() {
 
         audioStreamPlayback.playMedia(true, function() {
           audioStream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoAudioTwice.html
@@ -28,19 +28,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var testVideo = document.getElementById('testVideo');
       var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       streamPlayback.playMedia(false, function() {
 
         streamPlayback.playMedia(true, function() {
           stream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_playVideoTwice.html
@@ -29,19 +29,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo,
         videoStream);
 
       videoStreamPlayback.playMedia(false, function() {
 
         videoStreamPlayback.playMedia(true, function() {
           videoStream.stop();
           SimpleTest.finish();
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
     getUserMedia({audio: true}, function(stream) {
       var testAudio = document.getElementById('testAudio');
       var audioStreamPlayback = new LocalMediaStreamPlayback(testAudio, stream);
 
       audioStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish(new Error));
-    }, unexpectedCallbackAndFinish(new Error));
+        unexpectedCallbackAndFinish());
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopAudioStreamWithFollowupAudio.html
@@ -32,21 +32,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({audio: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish(new Error));
+          }, unexpectedCallbackAndFinish());
 
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * ended event fired.
    */
   runTest(function () {
     getUserMedia({video: true, audio: true}, function(stream) {
       var testVideo = document.getElementById('testVideo');
       var streamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       streamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish(new Error));
-    }, unexpectedCallbackAndFinish(new Error));
+        unexpectedCallbackAndFinish());
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html
@@ -32,21 +32,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({video: true, audio: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish(new Error));
+          }, unexpectedCallbackAndFinish());
 
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStream.html
@@ -25,16 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
    * call stop() on the stream, and successfully get an ended event fired.
    */
   runTest(function () {
     getUserMedia({video: true}, function(stream) {
       var testVideo = document.getElementById('testVideo');
       var videoStreamPlayback = new LocalMediaStreamPlayback(testVideo, stream);
 
       videoStreamPlayback.playMediaWithStreamStop(false, SimpleTest.finish,
-        unexpectedCallbackAndFinish(new Error));
-    }, unexpectedCallbackAndFinish(new Error));
+        unexpectedCallbackAndFinish());
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_stopVideoStreamWithFollowupVideo.html
@@ -33,21 +33,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 
       streamPlayback.playMediaWithStreamStop(false, function() {
         getUserMedia({video: true}, function(secondStream) {
           streamPlayback.mediaStream = secondStream;
 
           streamPlayback.playMedia(false, function() {
             secondStream.stop();
             SimpleTest.finish();
-          }, unexpectedCallbackAndFinish(new Error));
+          }, unexpectedCallbackAndFinish());
 
-        }, unexpectedCallbackAndFinish(new Error));
+        }, unexpectedCallbackAndFinish());
 
-      }, unexpectedCallbackAndFinish(new Error));
+      }, unexpectedCallbackAndFinish());
 
-    }, unexpectedCallbackAndFinish(new Error));
+    }, unexpectedCallbackAndFinish());
   });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_errorCallbacks.html
@@ -17,54 +17,48 @@
     return function (err) {
       ok(err, "Error is set");
       ok(err.name && err.name.length, "Error name = " + err.name);
       ok(err.message && err.message.length, "Error message = " + err.message);
       nextStep();
     }
   };
 
-  function successCallback(message) {
-    return function () {
-      unexpectedCallbackAndFinish(new Error)(message);
-    }
-  };
-
   function testCreateAnswerError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing createAnswer error callback");
-    pc.createAnswer(successCallback("createAnswer before offer should fail"),
+    pc.createAnswer(unexpectedCallbackAndFinish("createAnswer before offer should fail"),
                     errorCallback(testSetLocalDescriptionError));
   };
 
   function testSetLocalDescriptionError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing setLocalDescription error callback");
     pc.setLocalDescription(new mozRTCSessionDescription({ sdp: "Picklechips!",
                                                           type: "offer" }),
-      successCallback("setLocalDescription with nonsense SDP should fail"),
+      unexpectedCallbackAndFinish("setLocalDescription with nonsense SDP should fail"),
       errorCallback(testSetRemoteDescriptionError));
   };
 
   function testSetRemoteDescriptionError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing setRemoteDescription error callback");
     pc.setRemoteDescription(new mozRTCSessionDescription({ sdp: "Who?",
                                                            type: "offer" }),
-      successCallback("setRemoteDescription with nonsense SDP should fail"),
+      unexpectedCallbackAndFinish("setRemoteDescription with nonsense SDP should fail"),
       errorCallback(testAddIceCandidateError));
   };
 
   function testAddIceCandidateError() {
     var pc = new mozRTCPeerConnection();
     info ("Testing addIceCandidate error callback");
     pc.addIceCandidate(new mozRTCIceCandidate({ candidate: "Pony Lords, jump!",
                                                 sdpMid: "whee",
                                                 sdpMLineIndex: 1 }),
-      successCallback("addIceCandidate with nonsense candidate should fail"),
+      unexpectedCallbackAndFinish("addIceCandidate with nonsense candidate should fail"),
       errorCallback(SimpleTest.finish));
   };
 
   // No test for createOffer errors -- there's nothing we can do at this
   // level to evoke an error in createOffer.
 
   runTest(function () {
     testCreateAnswerError();
--- a/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html
+++ b/dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html
@@ -13,17 +13,17 @@
     bug: "857765",
     title: "Throw in PeerConnection callbacks"
   });
 
   let error_count = 0;
   let oldOnError = window.onerror;
   window.onerror = function (errorMsg, url, lineNumber) {
     if (errorMsg.indexOf("Expected") == -1) {
-      getFail(new Error)(errorMsg);
+      getFail()(errorMsg);
     }
     error_count += 1;
     info("onerror " + error_count + ": " + errorMsg);
     if (error_count == 7) {
       finish();
     }
     throw new Error("window.onerror may throw");
     return false;
@@ -31,54 +31,54 @@
 
   let pc0, pc1, pc2;
 
   runTest(function () {
     error_count = 0;
 
     // Test failure callbacks (limited to 1 for now)
     pc0 = new mozRTCPeerConnection();
-    pc0.createOffer(getFail(new Error), function(err) {
+    pc0.createOffer(getFail(), function(err) {
       pc1 = new mozRTCPeerConnection();
       pc2 = new mozRTCPeerConnection();
 
       // Test success callbacks (happy path)
       navigator.mozGetUserMedia({video:true, fake: true}, function(video1) {
         pc1.addStream(video1);
         pc1.createOffer(function(offer) {
           pc1.setLocalDescription(offer, function() {
             pc2.setRemoteDescription(offer, function() {
               pc2.createAnswer(function(answer) {
                 pc2.setLocalDescription(answer, function() {
                   pc1.setRemoteDescription(answer, function() {
                     throw new Error("Expected");
-                  }, getFail(new Error));
+                  }, getFail());
                   throw new Error("Expected");
-                }, getFail(new Error));
+                }, getFail());
                 throw new Error("Expected");
-              }, getFail(new Error));
+              }, getFail());
               throw new Error("Expected");
-            }, getFail(new Error));
+            }, getFail());
             throw new Error("Expected");
-          }, getFail(new Error));
+          }, getFail());
           throw new Error("Expected");
-        }, getFail(new Error));
-      }, getFail(new Error));
+        }, getFail());
+      }, getFail());
       throw new Error("Expected");
     });
   });
 
   function finish() {
     window.onerror = oldOnError;
     is(error_count, 7, "Seven expected errors verified.");
     SimpleTest.finish();
   }
 
-  function getFail(where) {
+  function getFail() {
     return function (err) {
       window.onerror = oldOnError;
-      unexpectedCallbackAndFinish(where)(err);
+      unexpectedCallbackAndFinish()(err);
     };
   }
 </script>
 </pre>
 </body>
 </html>
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -631,16 +631,20 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // isn't a chrome window.  Otherwise we can end up in a bizarre situation
     // where we can't shut down because an invisible window is open.  If
     // someone tries to do this, throw.
     if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
       nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
       nsCOMPtr<nsIWidget> parentWidget;
       if (parentWindow)
         parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
+      // NOTE: the logic for this visibility check is duplicated in
+      // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
+      // how a window is determined "visible" in this context then we should
+      // also adjust that attribute and/or any consumers of it...
       if (parentWidget && !parentWidget->IsVisible())
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     NS_ASSERTION(mWindowCreator,
                  "attempted to open a new window with no WindowCreator");
     rv = NS_ERROR_FAILURE;
     if (mWindowCreator) {
--- a/gfx/cairo/libpixman/src/Makefile.in
+++ b/gfx/cairo/libpixman/src/Makefile.in
@@ -12,17 +12,23 @@ include $(DEPTH)/config/autoconf.mk
 LIBRARY_NAME	= mozlibpixman
 MSVC_ENABLE_PGO := 1
 LIBXUL_LIBRARY = 1
 
 ifeq ($(OS_TARGET),Android)
 MODULE_OPTIMIZE_FLAGS = -O2
 endif
 
-DEFINES += -DPIXMAN_NO_TLS
+ifdef MOZ_USE_PTHREADS
+DEFINES += -DHAVE_PTHREAD_SETSPECIFIC
+endif
+
+ifdef _MSC_VER
+DEFINES += -DPIXMAN_USE_XP_DLL_TLS_WORKAROUND
+endif
 
 # Build MMX code either with VC or with gcc-on-x86
 ifdef _MSC_VER
 ifeq (86,$(findstring 86,$(OS_TEST)))
 ifneq (64,$(findstring 64,$(OS_TEST)))
 USE_MMX=1
 endif
 USE_SSE2=1
--- a/gfx/cairo/libpixman/src/pixman-compiler.h
+++ b/gfx/cairo/libpixman/src/pixman-compiler.h
@@ -114,20 +114,22 @@
 
 #elif defined(TLS)
 
 #   define PIXMAN_DEFINE_THREAD_LOCAL(type, name)			\
     static TLS type name
 #   define PIXMAN_GET_THREAD_LOCAL(name)				\
     (&name)
 
-#elif defined(__MINGW32__)
+#elif defined(__MINGW32__) || defined(PIXMAN_USE_XP_DLL_TLS_WORKAROUND)
 
 #   define _NO_W32_PSEUDO_MODIFIERS
 #   include <windows.h>
+#undef IN
+#undef OUT
 
 #   define PIXMAN_DEFINE_THREAD_LOCAL(type, name)			\
     static volatile int tls_ ## name ## _initialized = 0;		\
     static void *tls_ ## name ## _mutex = NULL;				\
     static unsigned tls_ ## name ## _index;				\
 									\
     static type *							\
     tls_ ## name ## _alloc (void)					\
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/pixman-xp-dll-workaround
@@ -0,0 +1,27 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-compiler.h b/gfx/cairo/libpixman/src/pixman-compiler.h
+--- a/gfx/cairo/libpixman/src/pixman-compiler.h
++++ b/gfx/cairo/libpixman/src/pixman-compiler.h
+@@ -114,20 +114,22 @@
+ 
+ #elif defined(TLS)
+ 
+ #   define PIXMAN_DEFINE_THREAD_LOCAL(type, name)			\
+     static TLS type name
+ #   define PIXMAN_GET_THREAD_LOCAL(name)				\
+     (&name)
+ 
+-#elif defined(__MINGW32__)
++#elif defined(__MINGW32__) || defined(PIXMAN_USE_XP_DLL_TLS_WORKAROUND)
+ 
+ #   define _NO_W32_PSEUDO_MODIFIERS
+ #   include <windows.h>
++#undef IN
++#undef OUT
+ 
+ #   define PIXMAN_DEFINE_THREAD_LOCAL(type, name)			\
+     static volatile int tls_ ## name ## _initialized = 0;		\
+     static void *tls_ ## name ## _mutex = NULL;				\
+     static unsigned tls_ ## name ## _index;				\
+ 									\
+     static type *							\
+     tls_ ## name ## _alloc (void)					\
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -150,16 +150,20 @@ public:
       ContainerRemoveChild(mFirstChild, this);
     }
 
     MOZ_COUNT_DTOR(ClientContainerLayer);
   }
 
   virtual void RenderLayer()
   {
+    if (GetMaskLayer()) {
+      ToClientLayer(GetMaskLayer())->RenderLayer();
+    }
+    
     // Setup mSupportsComponentAlphaChildren in the same way 
     // that ContainerLayerComposite will do.
     if (UseIntermediateSurface()) {
       if (GetEffectiveVisibleRegion().GetNumRects() != 1 ||
           !(GetContentFlags() & Layer::CONTENT_OPAQUE))
       {
         const gfx3DMatrix& transform3D = GetEffectiveTransform();
         gfxMatrix transform;
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -181,16 +181,17 @@ public:
 
   Layer* GetLayer() const { return mLayer; }
   void SetLayer(Layer* aLayer) { mLayer = aLayer; }
 
   virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
 
   virtual void Attach(Layer* aLayer, Compositor* aCompositor)
   {
+    MOZ_ASSERT(aCompositor, "Compositor is required");
     SetCompositor(aCompositor);
     SetLayer(aLayer);
   }
   void Detach() {
     SetLayer(nullptr);
     SetCompositor(nullptr);
   }
 
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -219,16 +219,20 @@ TiledContentHost::RenderLayerBuffer(Tile
                                     float aOpacity,
                                     const gfx::Point& aOffset,
                                     const gfx::Filter& aFilter,
                                     const gfx::Rect& aClipRect,
                                     const nsIntRegion& aMaskRegion,
                                     nsIntRect aVisibleRect,
                                     gfx::Matrix4x4 aTransform)
 {
+  if (!mCompositor) {
+    NS_WARNING("Can't render tiled content host - no compositor");
+    return;
+  }
   float resolution = aLayerBuffer.GetResolution();
   gfxSize layerScale(1, 1);
   // We assume that the current frame resolution is the one used in our primary
   // layer buffer. Compensate for a changing frame resolution.
   if (aLayerBuffer.GetFrameResolution() != mVideoMemoryTiledBuffer.GetFrameResolution()) {
     const gfxSize& layerResolution = aLayerBuffer.GetFrameResolution();
     const gfxSize& localResolution = mVideoMemoryTiledBuffer.GetFrameResolution();
     layerScale.width = layerResolution.width / localResolution.width;
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1032,16 +1032,20 @@ void AsyncPanZoomController::RequestCont
                          + mLastPaintRequestMetrics.mScrollOffset;
   CSSRect newDisplayPort = mFrameMetrics.mDisplayPort
                          + mFrameMetrics.mScrollOffset;
 
   if (fabsf(oldDisplayPort.x - newDisplayPort.x) < EPSILON &&
       fabsf(oldDisplayPort.y - newDisplayPort.y) < EPSILON &&
       fabsf(oldDisplayPort.width - newDisplayPort.width) < EPSILON &&
       fabsf(oldDisplayPort.height - newDisplayPort.height) < EPSILON &&
+      fabsf(mLastPaintRequestMetrics.mScrollOffset.x -
+            mFrameMetrics.mScrollOffset.x) < EPSILON &&
+      fabsf(mLastPaintRequestMetrics.mScrollOffset.y -
+            mFrameMetrics.mScrollOffset.y) < EPSILON &&
       mFrameMetrics.mResolution.width == mLastPaintRequestMetrics.mResolution.width) {
     return;
   }
 
   SendAsyncScrollEvent();
 
   // Cache the zoom since we're temporarily changing it for
   // acceleration-scaled painting.
@@ -1057,16 +1061,17 @@ void AsyncPanZoomController::RequestCont
   // This message is compressed, so fire whether or not we already have a paint
   // queued up. We need to know whether or not a paint was requested anyways,
   // for the purposes of content calling window.scrollTo().
   mPaintThrottler.PostTask(
     FROM_HERE,
     NewRunnableMethod(mGeckoContentController.get(),
                       &GeckoContentController::RequestContentRepaint,
                       mFrameMetrics));
+  mFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
   mLastPaintRequestMetrics = mFrameMetrics;
   mWaitingForContentToPaint = true;
 
   // Set the zoom back to what it was for the purpose of logic control.
   mFrameMetrics.mZoom = gfxSize(actualZoom, actualZoom);
 }
 
 void
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -173,17 +173,21 @@ struct JS_PUBLIC_API(NullPtr)
  * Encapsulated pointer class for use on the heap.
  *
  * Implements post barriers for heap-based GC thing pointers outside the engine.
  */
 template <typename T>
 class Heap : public js::HeapBase<T>
 {
   public:
-    Heap() { set(js::RootMethods<T>::initial()); }
+    Heap() {
+        MOZ_STATIC_ASSERT(sizeof(T) == sizeof(Heap<T>),
+                          "Heap<T> must be binary compatible with T.");
+        set(js::RootMethods<T>::initial());
+    }
     explicit Heap(T p) { set(p); }
     explicit Heap(const Heap<T> &p) { set(p.ptr); }
 
     ~Heap() {
         if (js::RootMethods<T>::needsPostBarrier(ptr))
             relocate();
     }
 
@@ -246,16 +250,18 @@ class MOZ_NONHEAP_CLASS Handle : public 
     friend class MutableHandle<T>;
 
   public:
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
     Handle(Handle<S> handle,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
+        MOZ_STATIC_ASSERT(sizeof(Handle<T>) == sizeof(T *),
+                          "Handle must be binary compatible with T*.");
         ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     /* Create a handle for a NULL pointer. */
     Handle(js::NullPtr) {
         MOZ_STATIC_ASSERT(mozilla::IsPointer<T>::value,
                           "js::NullPtr overload not valid for non-pointer types");
         ptr = reinterpret_cast<const T *>(&js::NullPtr::constNullValue);
@@ -875,16 +881,18 @@ Handle<T>::Handle(MutableHandle<S> &root
 {
     ptr = reinterpret_cast<const T *>(root.address());
 }
 
 template <typename T>
 inline
 MutableHandle<T>::MutableHandle(Rooted<T> *root)
 {
+    MOZ_STATIC_ASSERT(sizeof(MutableHandle<T>) == sizeof(T *),
+                      "MutableHandle must be binary compatible with T*.");
     ptr = root->address();
 }
 
 } /* namespace JS */
 
 namespace js {
 
 /*
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -512,17 +512,18 @@ JS_CONFIG_SUBSTITUTIONS=\
 	-DLIBRARY_NAME="$(LIBRARY_NAME)" \
 	-DJS_CONFIG_LIBS="$(JS_CONFIG_LIBS)" \
 	-DJS_CONFIG_MOZ_JS_LIBS="$(JS_CONFIG_MOZ_JS_LIBS)" \
 	-DMOZJS_MAJOR_VERSION="$(MOZJS_MAJOR_VERSION)" \
 	-DMOZJS_MINOR_VERSION="$(MOZJS_MINOR_VERSION)" \
 	-DMOZJS_PATCH_VERSION="$(MOZJS_PATCH_VERSION)" \
 	-DMOZJS_ALPHA="$(MOZJS_ALPHA)" \
 	-DNSPR_CFLAGS="$(NSPR_CFLAGS)" \
-	-DNSPR_PKGCONF_CHECK="$(NSPR_PKGCONF_CHECK)"
+	-DNSPR_PKGCONF_CHECK="$(NSPR_PKGCONF_CHECK)" \
+	-DUSE_CXX11="$(USE_CXX11)"
 
 $(JS_CONFIG_NAME): js-config.in Makefile $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk $(topsrcdir)/config/rules.mk
 	$(RM) $@.tmp
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py --marker % $(JS_CONFIG_SUBSTITUTIONS) $< > $@.tmp \
 	&& mv $@.tmp $@ && chmod +x $@
 
 SCRIPTS = $(JS_CONFIG_NAME)
 SDK_BINARY = $(JS_CONFIG_NAME)
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -246,17 +246,17 @@ private:
 	OP_FPU6				= 0xDD,
         OP_CALL_rel32                   = 0xE8,
         OP_JMP_rel32                    = 0xE9,
         PRE_SSE_F2                      = 0xF2,
         PRE_SSE_F3                      = 0xF3,
         OP_HLT                          = 0xF4,
         OP_GROUP3_EbIb                  = 0xF6,
         OP_GROUP3_Ev                    = 0xF7,
-        OP_GROUP3_EvIz                  = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test. 
+        OP_GROUP3_EvIz                  = 0xF7, // OP_GROUP3_Ev has an immediate, when instruction is a test.
         OP_GROUP5_Ev                    = 0xFF
     } OneByteOpcodeID;
 
     typedef enum {
         OP2_MOVSD_VsdWsd    = 0x10,
         OP2_MOVSD_WsdVsd    = 0x11,
         OP2_UNPCKLPS_VsdWsd = 0x14,
         OP2_CVTSI2SD_VsdEd  = 0x2A,
@@ -293,17 +293,17 @@ private:
         OP3_ROUNDSD_VsdWsd  = 0x0B,
         OP3_PTEST_VdVd      = 0x17,
         OP3_PINSRD_VsdWsd   = 0x22
     } ThreeByteOpcodeID;
 
     typedef enum {
         ESCAPE_PTEST        = 0x38,
         ESCAPE_PINSRD       = 0x3A,
-        ESCAPE_ROUNDSD      = 0x3A 
+        ESCAPE_ROUNDSD      = 0x3A
     } ThreeByteEscape;
 
     TwoByteOpcodeID jccRel32(Condition cond)
     {
         return (TwoByteOpcodeID)(OP2_JCC_rel32 + cond);
     }
 
     TwoByteOpcodeID setccOpcode(Condition cond)
@@ -337,17 +337,17 @@ private:
         GROUP5_OP_PUSH  = 6,
 
         FPU6_OP_FLD     = 0,
         FPU6_OP_FISTTP  = 1,
         FPU6_OP_FSTP    = 3,
 
         GROUP11_MOV = 0
     } GroupOpcodeID;
-    
+
     class X86InstructionFormatter;
 public:
 
     class JmpSrc {
         friend class X86Assembler;
         friend class X86InstructionFormatter;
     public:
         JmpSrc()
@@ -366,17 +366,17 @@ public:
 
         bool isSet() const {
             return m_offset != -1;
         }
 
     private:
         int m_offset;
     };
-    
+
     class JmpDst {
         friend class X86Assembler;
         friend class X86InstructionFormatter;
     public:
         JmpDst()
             : m_offset(-1)
             , m_used(false)
         {
@@ -816,17 +816,17 @@ public:
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, dst);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_SUB, dst);
             m_formatter.immediate32(imm);
         }
     }
-    
+
     void subl_im(int imm, int offset, RegisterID base)
     {
         spew("subl       $0x%x, %s0x%x(%s)",
              imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4, base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_SUB, base, offset);
             m_formatter.immediate8(imm);
         } else {
@@ -952,28 +952,28 @@ public:
         }
     }
 
     void sarl_CLr(RegisterID dst)
     {
         spew("sarl       %%cl, %s", nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst);
     }
-    
+
     void shrl_i8r(int imm, RegisterID dst)
     {
         spew("shrl       $%d, %s", imm, nameIReg(4, dst));
         if (imm == 1)
             m_formatter.oneByteOp(OP_GROUP2_Ev1, GROUP2_OP_SHR, dst);
         else {
             m_formatter.oneByteOp(OP_GROUP2_EvIb, GROUP2_OP_SHR, dst);
             m_formatter.immediate8(imm);
         }
     }
-    
+
     void shrl_CLr(RegisterID dst)
     {
         spew("shrl       %%cl, %s", nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst);
     }
 
     void shll_i8r(int imm, RegisterID dst)
     {
@@ -1051,47 +1051,47 @@ public:
         spew("imull      $%d, %s, %s",
              value, nameIReg(4, src), nameIReg(4, dst));
         m_formatter.oneByteOp(OP_IMUL_GvEvIz, dst, src);
         m_formatter.immediate32(value);
     }
 
     void idivl_r(RegisterID divisor)
     {
-        spew("idivl      %s", 
+        spew("idivl      %s",
              nameIReg(4, divisor));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_IDIV, divisor);
     }
 
     void divl_r(RegisterID divisor)
     {
         spew("div        %s",
              nameIReg(4, divisor));
         m_formatter.oneByteOp(OP_GROUP3_Ev, GROUP3_OP_DIV, divisor);
     }
 
     // Comparisons:
 
     void cmpl_rr(RegisterID src, RegisterID dst)
     {
-        spew("cmpl       %s, %s", 
+        spew("cmpl       %s, %s",
              nameIReg(4, src), nameIReg(4, dst));
         m_formatter.oneByteOp(OP_CMP_EvGv, src, dst);
     }
 
     void cmpl_rm(RegisterID src, int offset, RegisterID base)
     {
-        spew("cmpl       %s, %s0x%x(%s)", 
+        spew("cmpl       %s, %s0x%x(%s)",
              nameIReg(4, src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_CMP_EvGv, src, base, offset);
     }
 
     void cmpl_mr(int offset, RegisterID base, RegisterID src)
     {
-        spew("cmpl       %s0x%x(%s), %s", 
+        spew("cmpl       %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(4, base), nameIReg(src));
         m_formatter.oneByteOp(OP_CMP_GvEv, src, base, offset);
     }
 
     void cmpl_ir(int imm, RegisterID dst)
     {
         if (imm == 0) {
             testl_rr(dst, dst);
@@ -1109,36 +1109,36 @@ public:
     }
 
     void cmpl_ir_force32(int imm, RegisterID dst)
     {
         spew("cmpl       $0x%x, %s", imm, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, dst);
         m_formatter.immediate32(imm);
     }
-    
+
     void cmpl_im(int imm, int offset, RegisterID base)
     {
         spew("cmpl       $0x%x, %s0x%x(%s)",
              imm, PRETTY_PRINT_OFFSET(offset), nameIReg(4,base));
         if (CAN_SIGN_EXTEND_8_32(imm)) {
             m_formatter.oneByteOp(OP_GROUP1_EvIb, GROUP1_OP_CMP, base, offset);
             m_formatter.immediate8(imm);
         } else {
             m_formatter.oneByteOp(OP_GROUP1_EvIz, GROUP1_OP_CMP, base, offset);
             m_formatter.immediate32(imm);
         }
     }
-    
+
     void cmpb_im(int imm, int offset, RegisterID base)
     {
         m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, offset);
         m_formatter.immediate8(imm);
     }
-    
+
     void cmpb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         m_formatter.oneByteOp(OP_GROUP1_EbIb, GROUP1_OP_CMP, base, index, scale, offset);
         m_formatter.immediate8(imm);
     }
 
     void cmpl_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
@@ -1274,17 +1274,17 @@ public:
     }
 
     void testb_rr(RegisterID src, RegisterID dst)
     {
         spew("testb      %s, %s",
              nameIReg(1,src), nameIReg(1,dst));
         m_formatter.oneByteOp(OP_TEST_EbGb, src, dst);
     }
-    
+
     void testl_i32r(int imm, RegisterID dst)
     {
 #if WTF_CPU_X86_64
         // If the mask fits in an 8-bit immediate, we can use testb with an
         // 8-bit subreg. This could be extended to handle x86-32 too, but it
         // would require a check to see if the register supports 8-bit subregs.
         if (CAN_ZERO_EXTEND_8_32(imm)) {
             testb_i8r(imm, dst);
@@ -1299,23 +1299,23 @@ public:
 
     void testl_i32m(int imm, int offset, RegisterID base)
     {
         spew("testl      $0x%x, %s0x%x(%s)",
              imm, PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, offset);
         m_formatter.immediate32(imm);
     }
-    
+
     void testb_im(int imm, int offset, RegisterID base)
     {
         m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, offset);
         m_formatter.immediate8(imm);
     }
-    
+
     void testb_im(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         m_formatter.oneByteOp(OP_GROUP3_EbIb, GROUP3_OP_TEST, base, index, scale, offset);
         m_formatter.immediate8(imm);
     }
 
     void testl_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
@@ -1353,25 +1353,25 @@ public:
     }
 
     void testq_i32m(int imm, int offset, RegisterID base, RegisterID index, int scale)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp64(OP_GROUP3_EvIz, GROUP3_OP_TEST, base, index, scale, offset);
         m_formatter.immediate32(imm);
     }
-#endif 
+#endif
 
     void testw_rr(RegisterID src, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_TEST_EvGv, src, dst);
     }
-    
+
     void testb_i8r(int imm, RegisterID dst)
     {
         spew("testb      $0x%x, %s",
              imm, nameIReg(1,dst));
         m_formatter.oneByteOp8(OP_GROUP3_EbIb, GROUP3_OP_TEST, dst);
         m_formatter.immediate8(imm);
     }
 
@@ -1439,49 +1439,49 @@ public:
 
     void movw_rm(RegisterID src, int offset, RegisterID base)
     {
         spew("movw       %s, %s0x%x(%s)",
              nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
-    
+
     void movw_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         spew("movw       %s, %s0x%x(%s)",
              nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
-    
+
     void movl_rm(RegisterID src, int offset, RegisterID base)
     {
         spew("movl       %s, %s0x%x(%s)",
              nameIReg(4,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
 
     void movl_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
     void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        spew("movw       %s, %d(%s,%s,%d)", 
+        spew("movw       %s, %d(%s,%s,%d)",
              nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        spew("movl       %s, %d(%s,%s,%d)", 
+        spew("movl       %s, %d(%s,%s,%d)",
              nameIReg(4, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movl_mEAX(void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_MOV_EAXOv);
@@ -1682,24 +1682,24 @@ public:
 
     void movq_i64r(int64_t imm, RegisterID dst)
     {
         spew("movabsq    $0x%llx, %s",
              (unsigned long long int)imm, nameIReg(8,dst));
         m_formatter.oneByteOp64(OP_MOV_EAXIv, dst);
         m_formatter.immediate64(imm);
     }
-    
+
     void movsxd_rr(RegisterID src, RegisterID dst)
     {
         spew("movsxd     %s, %s",
              nameIReg(4, src), nameIReg(8, dst));
         m_formatter.oneByteOp64(OP_MOVSXD_GvEv, dst, src);
     }
-    
+
     JmpSrc movl_ripr(RegisterID dst)
     {
         spew("movl       \?(%%rip), %s",
              nameIReg(dst));
         m_formatter.oneByteRipOp(OP_MOV_GvEv, (RegisterID)dst, 0);
         return JmpSrc(m_formatter.size());
     }
 
@@ -1720,20 +1720,20 @@ public:
     }
 #else
     void movl_rm(RegisterID src, void* addr)
     {
         spew("movl       %s, 0(%p)",
              nameIReg(4, src), addr);
         if (src == X86Registers::eax)
             movl_EAXm(addr);
-        else 
+        else
             m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
     }
-    
+
     void movl_mr(void* addr, RegisterID dst)
     {
         spew("movl       0(%p), %s",
              addr, nameIReg(4, dst));
         if (dst == X86Registers::eax)
             movl_mEAX(addr);
         else
             m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
@@ -1896,25 +1896,25 @@ public:
 
     JmpSrc call()
     {
         m_formatter.oneByteOp(OP_CALL_rel32);
         JmpSrc r = m_formatter.immediateRel32();
         spew("call       ((%d))", r.m_offset);
         return r;
     }
-    
+
     JmpSrc call(RegisterID dst)
     {
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst);
         JmpSrc r = JmpSrc(m_formatter.size());
         spew("call       *%s", nameIReg(dst));
         return r;
     }
-    
+
     void call_m(int offset, RegisterID base)
     {
         spew("call       *%s0x%x(%s)",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset);
     }
 
     // Comparison of EAX against a 32-bit immediate. The immediate is patched
@@ -1930,28 +1930,28 @@ public:
 
     JmpSrc jmp()
     {
         m_formatter.oneByteOp(OP_JMP_rel32);
         JmpSrc r = m_formatter.immediateRel32();
         spew("jmp        ((%d))", r.m_offset);
         return r;
     }
-    
+
     // Return a JmpSrc so we have a label to the jump, so we can use this
     // To make a tail recursive call on x86-64.  The MacroAssembler
     // really shouldn't wrap this as a Jump, since it can't be linked. :-/
     JmpSrc jmp_r(RegisterID dst)
     {
         spew("jmp        *%s",
              nameIReg(dst));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, dst);
         return JmpSrc(m_formatter.size());
     }
-    
+
     void jmp_m(int offset, RegisterID base)
     {
         spew("jmp        *%d(%s)",
              offset, nameIReg(base));
         m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_JMPN, base, offset);
     }
 
     void jmp_m(int offset, RegisterID base, RegisterID index, int scale) {
@@ -1972,64 +1972,64 @@ public:
         m_formatter.immediate64(imm);
     }
 #endif
 
     JmpSrc jne()
     {
         return jCC(ConditionNE);
     }
-    
+
     JmpSrc jnz()
     {
         // printing done by jne()
         return jne();
     }
 
     JmpSrc je()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionE));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jz()
     {
         // printing done by je()
         return je();
     }
 
     JmpSrc jl()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionL));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jb()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionB));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jle()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionLE));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jbe()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionBE));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jge()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionGE));
         return m_formatter.immediateRel32();
     }
 
     JmpSrc jg()
@@ -2040,36 +2040,36 @@ public:
     }
 
     JmpSrc ja()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionA));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jae()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionAE));
         return m_formatter.immediateRel32();
     }
-    
+
     JmpSrc jo()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionO));
         return m_formatter.immediateRel32();
     }
 
     JmpSrc jp()
     {
         return jCC(ConditionP);
     }
-    
+
     JmpSrc js()
     {
         FIXME_INSN_PRINTING;
         m_formatter.twoByteOp(jccRel32(ConditionS));
         return m_formatter.immediateRel32();
     }
 
     JmpSrc jCC(Condition cond)
@@ -2323,25 +2323,25 @@ public:
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        spew("movsd       %s, %d(%s,%s,%d)", 
+        spew("movsd       %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movss_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        spew("movss       %s, %d(%s,%s,%d)", 
+        spew("movss       %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
         spew("movss      %d(%s,%s,%d), %s",
@@ -2422,17 +2422,17 @@ public:
         spew("movdqa     %s, %s0x%x(%s)",
              nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_WsdVsd, (RegisterID)src, base, offset);
     }
 
     void movdqa_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
-        spew("movdqa      %s, %d(%s,%s,%d)", 
+        spew("movdqa      %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_MOVDQA_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
     void movdqa_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movdqa     %s0x%x(%s), %s",
@@ -2630,22 +2630,22 @@ public:
         JmpDst r = JmpDst(m_formatter.size());
         spew("#label     ((%d))", r.m_offset);
         return r;
     }
 
     size_t currentOffset() const {
         return m_formatter.size();
     }
-    
+
     static JmpDst labelFor(JmpSrc jump, intptr_t offset = 0)
     {
         return JmpDst(jump.m_offset + offset);
     }
-    
+
     JmpDst align(int alignment)
     {
         spew(".balign %d", alignment);
         while (!m_formatter.isAligned(alignment))
             m_formatter.oneByteOp(OP_HLT);
 
         return label();
     }
@@ -2711,17 +2711,17 @@ public:
         if (oom())
             return;
 
         spew("##link     ((%d)) jumps to ((%d))",
              from.m_offset, to.m_offset);
         char* code = reinterpret_cast<char*>(m_formatter.data());
         setRel32(code + from.m_offset, code + to.m_offset);
     }
-    
+
     static void linkJump(void* code, JmpSrc from, void* to)
     {
         ASSERT(from.m_offset != -1);
 
         staticSpew("##link     ((%d)) jumps to ((%p))",
                    from.m_offset, to);
         setRel32(reinterpret_cast<char*>(code) + from.m_offset, to);
     }
@@ -2749,17 +2749,17 @@ public:
         setRel32(from, to);
     }
 
     static bool canRelinkJump(void* from, void* to)
     {
         intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from);
         return (offset == static_cast<int32_t>(offset));
     }
-    
+
     static void relinkCall(void* from, void* to)
     {
         staticSpew("##relinkCall ((from=%p)) ((to=%p))",
                    from, to);
         setRel32(from, to);
     }
 
     static void repatchInt32(void* where, int32_t value)
@@ -2783,17 +2783,17 @@ public:
 
 #if WTF_CPU_X86_64
         // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix.
         // Skip over the prefix byte.
         where = reinterpret_cast<char*>(where) + 1;
 #endif
         *reinterpret_cast<unsigned char*>(where) = static_cast<unsigned char>(OP_LEA);
     }
-    
+
     static void repatchLEAToLoadPtr(void* where)
     {
         staticSpew("##repatchLEAToLoadPtr ((where=%p))",
                    where);
 #if WTF_CPU_X86_64
         // On x86-64 pointer memory accesses require a 64-bit operand, and as such a REX prefix.
         // Skip over the prefix byte.
         where = reinterpret_cast<char*>(where) + 1;
@@ -2808,39 +2808,39 @@ public:
     }
 
     static void* getRelocatedAddress(void* code, JmpSrc jump)
     {
         ASSERT(jump.m_offset != -1);
 
         return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + jump.m_offset);
     }
-    
+
     static void* getRelocatedAddress(void* code, JmpDst destination)
     {
         ASSERT(destination.m_offset != -1);
 
         return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + destination.m_offset);
     }
-    
+
     static int getDifferenceBetweenLabels(JmpDst src, JmpDst dst)
     {
         return dst.m_offset - src.m_offset;
     }
-    
+
     static int getDifferenceBetweenLabels(JmpDst src, JmpSrc dst)
     {
         return dst.m_offset - src.m_offset;
     }
-    
+
     static int getDifferenceBetweenLabels(JmpSrc src, JmpDst dst)
     {
         return dst.m_offset - src.m_offset;
     }
-    
+
     void* executableAllocAndCopy(ExecutableAllocator* allocator, ExecutablePool **poolp, CodeKind kind)
     {
         return m_formatter.executableAllocAndCopy(allocator, poolp, kind);
     }
 
     void executableCopy(void* buffer)
     {
         memcpy(buffer, m_formatter.buffer(), size());
@@ -3337,17 +3337,17 @@ private:
         // Used to plant a REX byte with REX.w set (for 64-bit operations).
         inline void emitRexW(int r, int x, int b)
         {
             emitRex(true, r, x, b);
         }
 
         // Used for operations with byte operands - use byteRegRequiresRex() to check register operands,
         // regRequiresRex() to check other registers (i.e. address base & index).
-        // 
+        //
         // NB: WebKit's use of emitRexIf() is limited such that the reqRequiresRex() checks are
         // not needed. SpiderMonkey extends oneByteOp8 functionality such that r, x, and b can
         // all be used.
         inline void emitRexIf(bool condition, int r, int x, int b)
         {
             if (condition || regRequiresRex(r) || regRequiresRex(x) || regRequiresRex(b))
                 emitRex(false, r, x, b);
         }
@@ -3418,34 +3418,34 @@ private:
                     putModRm(ModRmMemoryDisp8, reg, base);
                     m_buffer.putByteUnchecked(offset);
                 } else {
                     putModRm(ModRmMemoryDisp32, reg, base);
                     m_buffer.putIntUnchecked(offset);
                 }
             }
         }
-    
+
         void memoryModRM_disp32(int reg, RegisterID base, int offset)
         {
             // A base of esp or r12 would be interpreted as a sib, so force a sib with no index & put the base in there.
 #if WTF_CPU_X86_64
             if ((base == hasSib) || (base == hasSib2))
 #else
             if (base == hasSib)
 #endif
             {
                 putModRmSib(ModRmMemoryDisp32, reg, base, noIndex, 0);
                 m_buffer.putIntUnchecked(offset);
             } else {
                 putModRm(ModRmMemoryDisp32, reg, base);
                 m_buffer.putIntUnchecked(offset);
             }
         }
-    
+
         void memoryModRM(int reg, RegisterID base, RegisterID index, int scale, int offset)
         {
             ASSERT(index != noIndex);
 
 #if WTF_CPU_X86_64
             if (!offset && (base != noBase) && (base != noBase2))
 #else
             if (!offset && (base != noBase))
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2327,16 +2327,48 @@ AC_CACHE_VAL(ac_cv_have_uname_us_domainn
 
 if test "$ac_cv_have_uname_us_domainname_field" = "true"; then
     AC_DEFINE(HAVE_UNAME_US_DOMAINNAME_FIELD)
     AC_MSG_RESULT(yes)
 else
     AC_MSG_RESULT(no)
 fi
 
+dnl Check whether we can use gcc's c++0x mode
+AC_LANG_CPLUSPLUS
+
+if test "$GNU_CXX"; then
+    _SAVE_CXXFLAGS=$CXXFLAGS
+    CXXFLAGS="$CXXFLAGS -std=gnu++0x"
+    USE_CXX11=1
+
+    AC_CACHE_CHECK(for gcc c++0x headers bug without rtti,
+        ac_cv_cxx0x_headers_bug,
+        [AC_TRY_COMPILE([#include <memory>], [],
+                        ac_cv_cxx0x_headers_bug="no",
+                        ac_cv_cxx0x_headers_bug="yes")])
+
+    if test "$CLANG_CXX" -a "$ac_cv_cxx0x_headers_bug" = "yes"; then
+        CXXFLAGS="$CXXFLAGS -I$_topsrcdir/build/unix/headers"
+        AC_CACHE_CHECK(whether workaround for gcc c++0x headers conflict with clang works,
+            ac_cv_cxx0x_clang_workaround,
+            [AC_TRY_COMPILE([#include <memory>], [],
+                            ac_cv_cxx0x_clang_workaround="yes",
+                            ac_cv_cxx0x_clang_workaround="no")])
+
+        if test "ac_cv_cxx0x_clang_workaround" = "no"; then
+            CXXFLAGS="$_SAVE_CXXFLAGS"
+            USE_CXX11=
+        fi
+    elif test "$ac_cv_cxx0x_headers_bug" = "yes"; then
+        CXXFLAGS="$_SAVE_CXXFLAGS"
+            USE_CXX11=
+    fi
+fi
+
 AC_LANG_C
 
 dnl Check for .hidden assembler directive and visibility attribute.
 dnl Borrowed from glibc configure.in
 dnl ===============================================================
 if test "$GNU_CC"; then
   AC_CACHE_CHECK(for visibility(hidden) attribute,
                  ac_cv_visibility_hidden,
@@ -4297,16 +4329,17 @@ AC_SUBST(TARGET_MD_ARCH)
 AC_SUBST(TARGET_XPCOM_ABI)
 AC_SUBST(OS_TARGET)
 AC_SUBST(OS_ARCH)
 AC_SUBST(OS_RELEASE)
 AC_SUBST(OS_TEST)
 AC_SUBST(CPU_ARCH)
 AC_SUBST(INTEL_ARCHITECTURE)
 
+AC_SUBST(USE_CXX11)
 AC_SUBST(WRAP_LDFLAGS)
 AC_SUBST(MKSHLIB)
 AC_SUBST(MKCSHLIB)
 AC_SUBST(MKSHLIB_FORCE_ALL)
 AC_SUBST(MKSHLIB_UNFORCE_ALL)
 AC_SUBST(DSO_CFLAGS)
 AC_SUBST(DSO_PIC_CFLAGS)
 AC_SUBST(DSO_LDOPTS)
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -394,16 +394,17 @@ frontend::CompileFunctionBody(JSContext 
 
     JS_ASSERT(!options.forEval);
 
     Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
                                     options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
     parser.sct = &sct;
 
     JS_ASSERT(fun);
+    JS_ASSERT(fun->isTenured());
 
     fun->setArgCount(formals.length());
 
     /* FIXME: make Function format the source for a function definition. */
     ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser.handler);
     if (!fn)
         return false;
 
--- a/js/src/frontend/ParseMaps.cpp
+++ b/js/src/frontend/ParseMaps.cpp
@@ -128,10 +128,10 @@ frontend::InitAtomMap(JSContext *cx, fro
             if (!atom)
                 continue;
             JS_ASSERT(it->value < indices->count());
             atoms[it->value].init(atom);
         }
     }
 }
 
-template class AtomDecls<FullParseHandler>;
-template class AtomDecls<SyntaxParseHandler>;
+template class js::frontend::AtomDecls<FullParseHandler>;
+template class js::frontend::AtomDecls<SyntaxParseHandler>;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -484,16 +484,18 @@ FunctionBox::FunctionBox(JSContext *cx, 
     asmStart(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     useAsm(false),
     insideUseAsm(outerpc && outerpc->useAsmOrInsideUseAsm()),
     funCxFlags()
 {
+    JS_ASSERT(fun->isTenured());
+
     if (!outerpc) {
         inWith = false;
 
     } else if (outerpc->parsingWith) {
         // This covers cases that don't involve eval().  For example:
         //
         //   with (o) { (function() { g(); })(); }
         //
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -665,20 +665,16 @@ const size_t ArenasPerChunk = ChunkBytes
 
 /* A chunk bitmap contains enough mark bits for all the cells in a chunk. */
 struct ChunkBitmap
 {
     volatile uintptr_t bitmap[ArenaBitmapWords * ArenasPerChunk];
 
   public:
     ChunkBitmap() { }
-    ChunkBitmap(MoveRef<ChunkBitmap> b) {
-        mozilla::PodArrayCopy(bitmap, b->bitmap);
-    }
-
 
     MOZ_ALWAYS_INLINE void getMarkWordAndMask(const Cell *cell, uint32_t color,
                                               uintptr_t **wordp, uintptr_t *maskp)
     {
         GetGCThingMarkWordAndMask(cell, color, wordp, maskp);
     }
 
     MOZ_ALWAYS_INLINE bool isMarked(const Cell *cell, uint32_t color) {
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -492,16 +492,19 @@ js::Nursery::markStoreBuffer(MinorCollec
     TraceHeapWithCallback(trc, MinorFallbackFixupCallback);
 }
 
 void
 js::Nursery::collect(JSRuntime *rt, JS::gcreason::Reason reason)
 {
     JS_AbortIfWrongThread(rt);
 
+    if (rt->mainThread.suppressGC)
+        return;
+
     if (!isEnabled())
         return;
 
     if (position() == start())
         return;
 
     rt->gcHelperThread.waitBackgroundSweepEnd();
 
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef JSGC_GENERATIONAL
 
 #include "jsgc.h"
 
 #include "gc/Barrier-inl.h"
 #include "gc/StoreBuffer.h"
+#include "vm/ForkJoin.h"
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 /*** SlotEdge ***/
 
 JS_ALWAYS_INLINE HeapSlot *
@@ -52,24 +53,29 @@ StoreBuffer::SlotEdge::inRememberedSet(N
 
 JS_ALWAYS_INLINE bool
 StoreBuffer::SlotEdge::isNullEdge() const
 {
     return !deref();
 }
 
 void
-StoreBuffer::WholeObjectEdges::mark(JSTracer *trc)
+StoreBuffer::WholeCellEdges::mark(JSTracer *trc)
 {
-    tenured->markChildren(trc);
+    JSGCTraceKind kind = GetGCThingTraceKind(tenured);
+    if (kind <= JSTRACE_OBJECT) {
+        MarkChildren(trc, static_cast<JSObject *>(tenured));
+        return;
+    }
+    JS_ASSERT(kind == JSTRACE_IONCODE);
+    static_cast<ion::IonCode *>(tenured)->trace(trc);
 }
 
 /*** MonoTypeBuffer ***/
 
-
 /* How full we allow a store buffer to become before we request a MinorGC. */
 const static double HighwaterRatio = 7.0 / 8.0;
 
 template <typename T>
 bool
 StoreBuffer::MonoTypeBuffer<T>::enable(uint8_t *region, size_t len)
 {
     JS_ASSERT(len % sizeof(T) == 0);
@@ -187,23 +193,23 @@ class AccumulateEdgesTracer : public JST
   public:
     AccumulateEdgesTracer(JSRuntime *rt, EdgeSet *edgesArg) : edges(edgesArg) {
         JS_TracerInit(this, rt, AccumulateEdgesTracer::tracer);
     }
 };
 
 template <>
 bool
-StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeObjectEdges>::accumulateEdges(EdgeSet &edges)
+StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>::accumulateEdges(EdgeSet &edges)
 {
     compact();
     AccumulateEdgesTracer trc(owner->runtime, &edges);
-    StoreBuffer::WholeObjectEdges *cursor = base;
+    StoreBuffer::WholeCellEdges *cursor = base;
     while (cursor != pos) {
-        cursor->tenured->markChildren(&trc);
+        cursor->mark(&trc);
         cursor++;
     }
     return true;
 }
 } /* namespace gc */
 } /* namespace js */
 
 /*** RelocatableMonoTypeBuffer ***/
@@ -342,19 +348,19 @@ StoreBuffer::enable()
     if (!bufferCell.enable(&asBytes[offset], CellBufferSize))
         return false;
     offset += CellBufferSize;
 
     if (!bufferSlot.enable(&asBytes[offset], SlotBufferSize))
         return false;
     offset += SlotBufferSize;
 
-    if (!bufferWholeObject.enable(&asBytes[offset], WholeObjectBufferSize))
+    if (!bufferWholeCell.enable(&asBytes[offset], WholeCellBufferSize))
         return false;
-    offset += WholeObjectBufferSize;
+    offset += WholeCellBufferSize;
 
     if (!bufferRelocVal.enable(&asBytes[offset], RelocValueBufferSize))
         return false;
     offset += RelocValueBufferSize;
 
     if (!bufferRelocCell.enable(&asBytes[offset], RelocCellBufferSize))
         return false;
     offset += RelocCellBufferSize;
@@ -375,17 +381,17 @@ StoreBuffer::disable()
     if (!enabled)
         return;
 
     aboutToOverflow = false;
 
     bufferVal.disable();
     bufferCell.disable();
     bufferSlot.disable();
-    bufferWholeObject.disable();
+    bufferWholeCell.disable();
     bufferRelocVal.disable();
     bufferRelocCell.disable();
     bufferGeneric.disable();
 
     js_free(buffer);
     enabled = false;
     overflowed = false;
 }
@@ -396,34 +402,34 @@ StoreBuffer::clear()
     if (!enabled)
         return true;
 
     aboutToOverflow = false;
 
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
-    bufferWholeObject.clear();
+    bufferWholeCell.clear();
     bufferRelocVal.clear();
     bufferRelocCell.clear();
     bufferGeneric.clear();
 
     return true;
 }
 
 void
 StoreBuffer::mark(JSTracer *trc)
 {
     JS_ASSERT(isEnabled());
     JS_ASSERT(!overflowed);
 
     bufferVal.mark(trc);
     bufferCell.mark(trc);
     bufferSlot.mark(trc);
-    bufferWholeObject.mark(trc);
+    bufferWholeCell.mark(trc);
     bufferRelocVal.mark(trc);
     bufferRelocCell.mark(trc);
     bufferGeneric.mark(trc);
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
@@ -447,17 +453,17 @@ StoreBuffer::coalesceForVerification()
     }
     JS_ASSERT(edgeSet.empty());
     if (!bufferVal.accumulateEdges(edgeSet))
         return false;
     if (!bufferCell.accumulateEdges(edgeSet))
         return false;
     if (!bufferSlot.accumulateEdges(edgeSet))
         return false;
-    if (!bufferWholeObject.accumulateEdges(edgeSet))
+    if (!bufferWholeCell.accumulateEdges(edgeSet))
         return false;
     if (!bufferRelocVal.accumulateEdges(edgeSet))
         return false;
     if (!bufferRelocCell.accumulateEdges(edgeSet))
         return false;
     return true;
 }
 
@@ -468,16 +474,22 @@ StoreBuffer::containsEdgeAt(void *loc) c
 }
 
 void
 StoreBuffer::releaseVerificationData()
 {
     edgeSet.finish();
 }
 
+bool
+StoreBuffer::inParallelSection() const
+{
+    return InParallelSection();
+}
+
 JS_PUBLIC_API(void)
 JS::HeapCellPostBarrier(js::gc::Cell **cellp)
 {
     JS_ASSERT(*cellp);
     JSRuntime *runtime = (*cellp)->runtime();
     runtime->gcStoreBuffer.putRelocatableCell(cellp);
 }
 
@@ -505,13 +517,13 @@ JS::HeapValueRelocate(JS::Value *valuep)
     JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep));
     JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtime();
     runtime->gcStoreBuffer.removeRelocatableValue(valuep);
 }
 
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotEdge>;
-template class StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeObjectEdges>;
+template class StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>;
 template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::ValueEdge>;
 template class StoreBuffer::RelocatableMonoTypeBuffer<StoreBuffer::CellPtrEdge>;
 
 #endif /* JSGC_GENERATIONAL */
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -176,16 +176,18 @@ class StoreBuffer
         /*
          * Attempts to reduce the usage of the buffer by removing unnecessary
          * entries.
          */
         virtual void compact();
 
         /* Add one item to the buffer. */
         void put(const T &v) {
+            JS_ASSERT(!owner->inParallelSection());
+
             /* Check if we have been enabled. */
             if (!pos)
                 return;
 
             /*
              * Note: it is sometimes valid for a put to happen in the middle of a GC,
              * e.g. a rekey of a Relocatable may end up here. In general, we do not
              * care about these new entries or any overflows they cause.
@@ -224,16 +226,17 @@ class StoreBuffer
         {}
 
         /* Override compaction to filter out removed items. */
         void compactMoved();
         virtual void compact();
 
         /* Record a removal from the buffer. */
         void unput(const T &v) {
+            JS_ASSERT(!this->owner->inParallelSection());
             MonoTypeBuffer<T>::put(v.tagged());
         }
     };
 
     class GenericBuffer
     {
         friend class StoreBuffer;
 
@@ -256,16 +259,18 @@ class StoreBuffer
         /* Mark all generic edges. */
         void mark(JSTracer *trc);
 
         /* Check if a pointer is present in the buffer. */
         bool containsEdge(void *location) const;
 
         template <typename T>
         void put(const T &t) {
+            JS_ASSERT(!owner->inParallelSection());
+
             /* Check if we have been enabled. */
             if (!pos)
                 return;
 
             /* Check for overflow. */
             if (top - pos < (unsigned)(sizeof(unsigned) + sizeof(T))) {
                 owner->setOverflowed();
                 return;
@@ -371,45 +376,45 @@ class StoreBuffer
         template <typename NurseryType>
         JS_ALWAYS_INLINE bool inRememberedSet(NurseryType *nursery) const;
 
         JS_ALWAYS_INLINE bool isNullEdge() const;
 
         void mark(JSTracer *trc);
     };
 
-    class WholeObjectEdges
+    class WholeCellEdges
     {
         friend class StoreBuffer;
-        friend class StoreBuffer::MonoTypeBuffer<WholeObjectEdges>;
+        friend class StoreBuffer::MonoTypeBuffer<WholeCellEdges>;
 
-        JSObject *tenured;
+        Cell *tenured;
 
-        WholeObjectEdges(JSObject *obj) : tenured(obj) {
+        WholeCellEdges(Cell *cell) : tenured(cell) {
             JS_ASSERT(tenured->isTenured());
         }
 
-        bool operator==(const WholeObjectEdges &other) const { return tenured == other.tenured; }
-        bool operator!=(const WholeObjectEdges &other) const { return tenured != other.tenured; }
+        bool operator==(const WholeCellEdges &other) const { return tenured == other.tenured; }
+        bool operator!=(const WholeCellEdges &other) const { return tenured != other.tenured; }
 
         template <typename NurseryType>
         bool inRememberedSet(NurseryType *nursery) const { return true; }
 
         /* This is used by RemoveDuplicates as a unique pointer to this Edge. */
         void *location() const { return (void *)tenured; }
 
         bool isNullEdge() const { return false; }
 
         void mark(JSTracer *trc);
     };
 
     MonoTypeBuffer<ValueEdge> bufferVal;
     MonoTypeBuffer<CellPtrEdge> bufferCell;
     MonoTypeBuffer<SlotEdge> bufferSlot;
-    MonoTypeBuffer<WholeObjectEdges> bufferWholeObject;
+    MonoTypeBuffer<WholeCellEdges> bufferWholeCell;
     RelocatableMonoTypeBuffer<ValueEdge> bufferRelocVal;
     RelocatableMonoTypeBuffer<CellPtrEdge> bufferRelocCell;
     GenericBuffer bufferGeneric;
 
     JSRuntime *runtime;
 
     void *buffer;
 
@@ -419,28 +424,28 @@ class StoreBuffer
 
     /* For the verifier. */
     EdgeSet edgeSet;
 
     /* TODO: profile to find the ideal size for these. */
     static const size_t ValueBufferSize = 1 * 1024 * sizeof(ValueEdge);
     static const size_t CellBufferSize = 2 * 1024 * sizeof(CellPtrEdge);
     static const size_t SlotBufferSize = 2 * 1024 * sizeof(SlotEdge);
-    static const size_t WholeObjectBufferSize = 2 * 1024 * sizeof(WholeObjectEdges);
+    static const size_t WholeCellBufferSize = 2 * 1024 * sizeof(WholeCellEdges);
     static const size_t RelocValueBufferSize = 1 * 1024 * sizeof(ValueEdge);
     static const size_t RelocCellBufferSize = 1 * 1024 * sizeof(CellPtrEdge);
     static const size_t GenericBufferSize = 1 * 1024 * sizeof(int);
     static const size_t TotalSize = ValueBufferSize + CellBufferSize +
-                                    SlotBufferSize + WholeObjectBufferSize +
+                                    SlotBufferSize + WholeCellBufferSize +
                                     RelocValueBufferSize + RelocCellBufferSize +
                                     GenericBufferSize;
 
   public:
     explicit StoreBuffer(JSRuntime *rt)
-      : bufferVal(this), bufferCell(this), bufferSlot(this), bufferWholeObject(this),
+      : bufferVal(this), bufferCell(this), bufferSlot(this), bufferWholeCell(this),
         bufferRelocVal(this), bufferRelocCell(this), bufferGeneric(this),
         runtime(rt), buffer(NULL), aboutToOverflow(false), overflowed(false),
         enabled(false)
     {}
 
     bool enable();
     void disable();
     bool isEnabled() { return enabled; }
@@ -456,18 +461,18 @@ class StoreBuffer
         bufferVal.put(v);
     }
     void putCell(Cell **o) {
         bufferCell.put(o);
     }
     void putSlot(JSObject *obj, HeapSlot::Kind kind, uint32_t slot) {
         bufferSlot.put(SlotEdge(obj, kind, slot));
     }
-    void putWholeObject(JSObject *obj) {
-        bufferWholeObject.put(WholeObjectEdges(obj));
+    void putWholeCell(Cell *cell) {
+        bufferWholeCell.put(WholeCellEdges(cell));
     }
 
     /* Insert or update a single edge in the Relocatable buffer. */
     void putRelocatableValue(Value *v) {
         bufferRelocVal.put(v);
     }
     void putRelocatableCell(Cell **c) {
         bufferRelocCell.put(c);
@@ -488,16 +493,19 @@ class StoreBuffer
     /* Mark the source of all edges in the store buffer. */
     void mark(JSTracer *trc);
 
     /* For the verifier. */
     bool coalesceForVerification();
     void releaseVerificationData();
     bool containsEdgeAt(void *loc) const;
 
+    /* We cannot call InParallelSection directly because of a circular dependency. */
+    bool inParallelSection() const;
+
     /* For use by our owned buffers and for testing. */
     void setAboutToOverflow();
     void setOverflowed();
 };
 
 } /* namespace gc */
 } /* namespace js */
 
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -24,18 +24,16 @@ using namespace mozilla;
 #include "ion/MIRGraph.h"
 
 using namespace js::ion;
 
 #ifdef MOZ_VTUNE
 # include "jitprofiling.h"
 #endif
 
-#ifdef JS_ASMJS
-
 /*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode *
 NextNode(ParseNode *pn)
 {
     return pn->pn_next;
 }
@@ -929,48 +927,53 @@ class MOZ_STACK_CLASS ModuleCompiler
   public:
     class Func
     {
         ParseNode *fn_;
         ParseNode *body_;
         MIRTypeVector argTypes_;
         RetType returnType_;
         mutable Label code_;
+        unsigned compileTime_;
 
       public:
         Func(ParseNode *fn, ParseNode *body, MoveRef<MIRTypeVector> types, RetType returnType)
           : fn_(fn),
             body_(body),
             argTypes_(types),
             returnType_(returnType),
-            code_()
+            code_(),
+            compileTime_(0)
         {}
 
         Func(MoveRef<Func> rhs)
           : fn_(rhs->fn_),
             body_(rhs->body_),
             argTypes_(Move(rhs->argTypes_)),
             returnType_(rhs->returnType_),
-            code_(rhs->code_)
+            code_(rhs->code_),
+            compileTime_(rhs->compileTime_)
         {}
 
         ~Func()
         {
             // Avoid spurious Label assertions on compilation failure.
             if (!code_.bound())
                 code_.bind(0);
         }
 
         ParseNode *fn() const { return fn_; }
         ParseNode *body() const { return body_; }
         unsigned numArgs() const { return argTypes_.length(); }
         VarType argType(unsigned i) const { return VarType::FromMIRType(argTypes_[i]); }
         const MIRTypeVector &argMIRTypes() const { return argTypes_; }
         RetType returnType() const { return returnType_; }
         Label *codeLabel() const { return &code_; }
+        unsigned compileTime() const { return compileTime_; }
+        void accumulateCompileTime(unsigned ms) { compileTime_ += ms; }
     };
 
     class Global
     {
       public:
         enum Which { Variable, Function, FuncPtrTable, FFI, ArrayView, MathBuiltin, Constant };
 
       private:
@@ -1097,20 +1100,29 @@ class MOZ_STACK_CLASS ModuleCompiler
             }
             return true;
         }
     };
 
     typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor, ContextAllocPolicy> ExitMap;
 
   private:
+    struct SlowFunction
+    {
+        PropertyName *name;
+        unsigned ms;
+        unsigned line;
+        unsigned column;
+    };
+
     typedef HashMap<PropertyName*, AsmJSMathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, Global> GlobalMap;
     typedef Vector<Func> FuncVector;
     typedef Vector<AsmJSGlobalAccess> GlobalAccessVector;
+    typedef Vector<SlowFunction> SlowFunctionVector;
 
     JSContext *                    cx_;
     MacroAssembler                 masm_;
 
     ScopedJSDeletePtr<AsmJSModule> module_;
 
     PropertyName *                 moduleFunctionName_;
 
@@ -1120,16 +1132,20 @@ class MOZ_STACK_CLASS ModuleCompiler
     ExitMap                        exits_;
     MathNameMap                    standardLibraryMathNames_;
     GlobalAccessVector             globalAccesses_;
     Label                          stackOverflowLabel_;
     Label                          operationCallbackLabel_;
 
     char *                         errorString_;
     ParseNode *                    errorNode_;
+
+    int64_t                        usecBefore_;
+    SlowFunctionVector             slowFunctions_;
+
     TokenStream &                  tokenStream_;
 
     DebugOnly<int>                 currentPass_;
 
     bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) {
         JSAtom *atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
@@ -1144,26 +1160,28 @@ class MOZ_STACK_CLASS ModuleCompiler
         globals_(cx),
         functions_(cx),
         funcPtrTables_(cx),
         exits_(cx),
         standardLibraryMathNames_(cx),
         globalAccesses_(cx),
         errorString_(NULL),
         errorNode_(NULL),
+        usecBefore_(PRMJ_Now()),
+        slowFunctions_(cx),
         tokenStream_(ts),
         currentPass_(1)
     {}
 
     ~ModuleCompiler() {
         if (errorString_) {
             tokenStream_.reportAsmJSError(errorNode_->pn_pos.begin,
                                           JSMSG_USE_ASM_TYPE_FAIL,
                                           errorString_);
-            JS_smprintf_free(errorString_);
+            js_free(errorString_);
         }
 
         // Avoid spurious Label assertions on compilation failure.
         if (!stackOverflowLabel_.bound())
             stackOverflowLabel_.bind(0);
         if (!operationCallbackLabel_.bound())
             operationCallbackLabel_.bind(0);
     }
@@ -1232,16 +1250,28 @@ class MOZ_STACK_CLASS ModuleCompiler
 
     bool failName(ParseNode *pn, const char *fmt, PropertyName *name) {
         JSAutoByteString bytes(cx_, name);
         if (bytes.ptr())
             failf(pn, fmt, bytes.ptr());
         return false;
     }
 
+    static const unsigned SLOW_FUNCTION_THRESHOLD_MS = 250;
+
+    bool maybeReportCompileTime(ParseNode *fn, unsigned ms) {
+        if (ms < SLOW_FUNCTION_THRESHOLD_MS)
+            return true;
+        SlowFunction sf;
+        sf.name = FunctionName(fn);
+        sf.ms = ms;
+        tokenStream_.srcCoords.lineNumAndColumnIndex(fn->pn_pos.begin, &sf.line, &sf.column);
+        return slowFunctions_.append(sf);
+    }
+
     /*************************************************** Read-only interface */
 
     JSContext *cx() const { return cx_; }
     MacroAssembler &masm() { return masm_; }
     Label &stackOverflowLabel() { return stackOverflowLabel_; }
     Label &operationCallbackLabel() { return operationCallbackLabel_; }
     bool hasError() const { return errorString_ != NULL; }
     const AsmJSModule &module() const { return *module_.get(); }
@@ -1453,16 +1483,41 @@ class MOZ_STACK_CLASS ModuleCompiler
     void setEntryOffset(unsigned exportIndex) {
         JS_ASSERT(currentPass_ == 3);
 #if defined(JS_CPU_ARM)
         masm_.flush();
 #endif
         module_->exportedFunction(exportIndex).initCodeOffset(masm_.size());
     }
 
+    void buildCompilationTimeReport(ScopedJSFreePtr<char> *out) {
+        int64_t usecAfter = PRMJ_Now();
+        int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
+        ScopedJSFreePtr<char> slowFuns;
+        if (!slowFunctions_.empty()) {
+            slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
+            if (!slowFuns)
+                return;
+            for (unsigned i = 0; i < slowFunctions_.length(); i++) {
+                SlowFunction &func = slowFunctions_[i];
+                JSAutoByteString name;
+                if (!js_AtomToPrintableString(cx_, func.name, &name))
+                    return;
+                slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums, %g%%)%s", slowFuns.get(),
+                                           name.ptr(), func.line, func.column, func.ms,
+                                           double(func.ms)/double(msTotal),
+                                           i+1 < slowFunctions_.length() ? ", " : ""));
+                if (!slowFuns)
+                    return;
+            }
+        }
+        out->reset(JS_smprintf("total compilation time %dms%s",
+                               msTotal, slowFuns ? slowFuns.get() : ""));
+    }
+
     bool finish(ScopedJSDeletePtr<AsmJSModule> *module) {
         // After finishing, the only valid operation on an ModuleCompiler is
         // destruction.
         JS_ASSERT(currentPass_ == 3);
         currentPass_ = -1;
 
         // Finish the code section.
         masm_.finish();
@@ -3080,18 +3135,22 @@ CheckModuleExportObject(ModuleCompiler &
     return true;
 }
 
 static bool
 CheckModuleExports(ModuleCompiler &m, ParseNode *fn, ParseNode **stmtIter)
 {
     ParseNode *returnNode = SkipEmptyStatements(*stmtIter);
 
-    if (!returnNode || !returnNode->isKind(PNK_RETURN))
-        return m.fail(fn, "asm.js module must end with a return export statement");
+    if (!returnNode || !returnNode->isKind(PNK_RETURN)) {
+        if (returnNode && NextNode(returnNode) != NULL)
+            return m.fail(returnNode, "invalid asm.js statement");
+        else
+            return m.fail(fn, "asm.js module must end with a return export statement");
+    }
 
     ParseNode *returnExpr = UnaryKid(returnNode);
 
     if (!returnExpr)
         return m.fail(returnNode, "export statement must return something");
 
     if (returnExpr->isKind(PNK_OBJECT)) {
         if (!CheckModuleExportObject(m, returnExpr))
@@ -3241,19 +3300,29 @@ CheckArrayLoad(FunctionCompiler &f, Pars
 static bool
 CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
     if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef))
         return false;
 
+    Use use;
+    switch (TypedArrayStoreType(viewType)) {
+      case ArrayStore_Intish:
+        use = Use::ToInt32;
+        break;
+      case ArrayStore_Doublish:
+        use = Use::ToNumber;
+        break;
+    }
+
     MDefinition *rhsDef;
     Type rhsType;
-    if (!CheckExpr(f, rhs, Use::NoCoercion, &rhsDef, &rhsType))
+    if (!CheckExpr(f, rhs, use, &rhsDef, &rhsType))
         return false;
 
     switch (TypedArrayStoreType(viewType)) {
       case ArrayStore_Intish:
         if (!rhsType.isIntish())
             return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
         break;
       case ArrayStore_Doublish:
@@ -4626,16 +4695,18 @@ CheckVariableDecls(ModuleCompiler &m, Fu
 
     *stmtIter = stmt;
     return true;
 }
 
 static MIRGenerator *
 CheckFunctionBody(ModuleCompiler &m, ModuleCompiler::Func &func, LifoAlloc &lifo)
 {
+    int64_t before = PRMJ_Now();
+
     // CheckFunctionSignature already has already checked the
     // function head as well as argument type declarations. The ParseNode*
     // stored in f.body points to the first non-argument statement.
     ParseNode *stmtIter = func.body();
 
     FunctionCompiler::LocalMap locals(m.cx());
     if (!locals.init())
         return NULL;
@@ -4668,23 +4739,27 @@ CheckFunctionBody(ModuleCompiler &m, Mod
         return NULL;
 
     if (!CheckStatements(f, stmtIter))
         return NULL;
 
     f.returnVoid();
     JS_ASSERT(!tempAlloc->rootList());
 
+    func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
+
     return mirGen;
 }
 
 static bool
 GenerateAsmJSCode(ModuleCompiler &m, ModuleCompiler::Func &func,
                   MIRGenerator &mirGen, LIRGraph &lir)
 {
+    int64_t before = PRMJ_Now();
+
     m.masm().bind(func.codeLabel());
 
     ScopedJSDeletePtr<CodeGenerator> codegen(GenerateCode(&mirGen, &lir, &m.masm()));
     if (!codegen)
         return m.fail(func.fn(), "internal codegen failure (probably out of memory)");
 
     if (!m.collectAccesses(mirGen))
         return false;
@@ -4707,16 +4782,20 @@ GenerateAsmJSCode(ModuleCompiler &m, Mod
     // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
     // after each function is compiled. This method is responsible for cleaning
     // out any dangling pointers that the MacroAssembler may have kept.
     m.masm().resetForNewCodeGenerator();
 
     // Align internal function headers.
     m.masm().align(CodeAlignment);
 
+    func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
+    if (!m.maybeReportCompileTime(func.fn(), func.compileTime()))
+        return false;
+
     // Unlike regular IonMonkey which links and generates a new IonCode for
     // every function, we accumulate all the functions in the module in a
     // single MacroAssembler and link at end. Linking asm.js doesn't require a
     // CodeGenerator so we can destroy it now.
     return true;
 }
 
 static const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
@@ -4736,23 +4815,27 @@ CheckFunctionBodiesSequential(ModuleComp
         MIRGenerator *mirGen = CheckFunctionBody(m, func, lifo);
         if (!mirGen)
             return false;
 
         IonSpewNewFunction(&mirGen->graph(), NullPtr());
 
         IonContext icx(m.cx()->compartment(), &mirGen->temp());
 
+        int64_t before = PRMJ_Now();
+
         if (!OptimizeMIR(mirGen))
             return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
 
         LIRGraph *lir = GenerateLIR(mirGen);
         if (!lir)
             return m.fail(func.fn(), "internal compiler failure (probably out of memory)");
 
+        func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
+
         if (!GenerateAsmJSCode(m, func, *mirGen, *lir))
             return false;
 
         IonSpewEndFunction();
     }
 
     return true;
 }
@@ -4791,18 +4874,22 @@ GetFinishedCompilation(ModuleCompiler &m
 static bool
 GenerateCodeForFinishedJob(ModuleCompiler &m, ParallelGroupState &group, AsmJSParallelTask **outTask)
 {
     // Block until a used LifoAlloc becomes available.
     AsmJSParallelTask *task = GetFinishedCompilation(m, group);
     if (!task)
         return false;
 
+    ModuleCompiler::Func &func = m.function(task->funcNum);
+
+    func.accumulateCompileTime(task->compileTime);
+
     // Perform code generation on the main thread.
-    if (!GenerateAsmJSCode(m, m.function(task->funcNum), *task->mir, *task->lir))
+    if (!GenerateAsmJSCode(m, func, *task->mir, *task->lir))
         return false;
     group.compiledJobs++;
 
     // Clear the LifoAlloc for use by another worker.
     TempAllocator &tempAlloc = task->mir->temp();
     tempAlloc.TempAllocator::~TempAllocator();
     task->lifo.releaseAll();
 
@@ -5226,19 +5313,21 @@ GenerateEntries(ModuleCompiler &m)
         if (!GenerateEntry(m, m.module().exportedFunction(i)))
             return false;
     }
 
     return true;
 }
 
 static inline bool
-TryEnablingIon(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv) {
-
-    JSScript *script = exitDatum->fun->nonLazyScript();
+TryEnablingIon(JSContext *cx, AsmJSModule::ExitDatum *exitDatum, int32_t argc, Value *argv)
+{
+    JSScript *script = exitDatum->fun->maybeNonLazyScript();
+    if (!script)
+        return true;
 
     // Test if the function is Ion compiled
     if (!script->hasIonScript())
         return true;
 
     // Currently we can't rectify arguments. Therefore disabling if argc is too low.
     if (exitDatum->fun->nargs > argc)
         return true;
@@ -5987,17 +6076,18 @@ GenerateExits(ModuleCompiler &m)
 
     GenerateOperationCallbackExit(m, &throwLabel);
 
     GenerateThrowExit(m, &throwLabel);
     return true;
 }
 
 static bool
-CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<AsmJSModule> *module)
+CheckModule(JSContext *cx, TokenStream &ts, ParseNode *fn, ScopedJSDeletePtr<AsmJSModule> *module,
+            ScopedJSFreePtr<char> *compilationTimeReport)
 {
     ModuleCompiler m(cx, ts);
     if (!m.init())
         return false;
 
     if (PropertyName *moduleFunctionName = FunctionName(fn)) {
         if (!CheckModuleLevelName(m, moduleFunctionName, fn))
             return false;
@@ -6048,23 +6138,25 @@ CheckModule(JSContext *cx, TokenStream &
     m.setSecondPassComplete();
 
     if (!GenerateEntries(m))
         return false;
 
     if (!GenerateExits(m))
         return false;
 
-    return m.finish(module);
-}
-
-#endif // defined(JS_ASMJS)
+    if (!m.finish(module))
+        return false;
+
+    m.buildCompilationTimeReport(compilationTimeReport);
+    return true;
+}
 
 static bool
-Warn(JSContext *cx, int code, const char *str = NULL)
+Warn(JSContext *cx, int code, const char *str)
 {
     return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
                                         NULL, code, str);
 }
 
 extern bool
 EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt);
 
@@ -6077,29 +6169,29 @@ js::CompileAsmJS(JSContext *cx, TokenStr
         return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
 
     if (!cx->hasOption(JSOPTION_ASMJS))
         return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
 
     if (cx->compartment()->debugMode())
         return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
 
-#ifdef JS_ASMJS
     if (!EnsureAsmJSSignalHandlersInstalled(cx->runtime()))
         return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
 
 # ifdef JS_PARALLEL_COMPILATION
     if (OffThreadCompilationEnabled(cx)) {
         if (!EnsureParallelCompilationInitialized(cx->runtime()))
             return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Failed compilation thread initialization");
     }
 # endif
 
+    ScopedJSFreePtr<char> compilationTimeReport;
     ScopedJSDeletePtr<AsmJSModule> module;
-    if (!CheckModule(cx, ts, fn, &module))
+    if (!CheckModule(cx, ts, fn, &module, &compilationTimeReport))
         return !cx->isExceptionPending();
 
     module->initPostLinkFailureInfo(options, scriptSource, bufStart, bufEnd);
 
     RootedObject moduleObj(cx, NewAsmJSModuleObject(cx, &module));
     if (!moduleObj)
         return false;
 
@@ -6112,34 +6204,27 @@ js::CompileAsmJS(JSContext *cx, TokenStr
     moduleFun.set(NewFunction(cx, NullPtr(), LinkAsmJS, FunctionNumFormals(fn),
                               JSFunction::NATIVE_FUN, NullPtr(), name,
                               JSFunction::ExtendedFinalizeKind, TenuredObject));
     if (!moduleFun)
         return false;
 
     SetAsmJSModuleObject(moduleFun, moduleObj);
 
-    return Warn(cx, JSMSG_USE_ASM_TYPE_OK);
-#else
-    return Warn(cx, JSMSG_USE_ASM_TYPE_FAIL, "Platform not supported (yet)");
-#endif
+    return Warn(cx, JSMSG_USE_ASM_TYPE_OK, compilationTimeReport);
 }
 
 JSBool
 js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-#ifdef JS_ASMJS
     bool available = JSC::MacroAssembler().supportsFloatingPoint() &&
                      !cx->compartment()->debugMode() &&
                      cx->hasOption(JSOPTION_ASMJS);
-#else
-    bool available = false;
-#endif
 
     args.rval().set(BooleanValue(available));
     return true;
 }
 
 static bool
 IsMaybeWrappedNativeFunction(const Value &v, Native native)
 {
--- a/js/src/ion/AsmJS.h
+++ b/js/src/ion/AsmJS.h
@@ -7,20 +7,16 @@
 #if !defined(jsion_asmjs_h__)
 #define jsion_asmjs_h__
 
 #ifdef XP_MACOSX
 # include <pthread.h>
 # include <mach/mach.h>
 #endif
 
-#if defined(JS_ION)
-# define JS_ASMJS
-#endif
-
 namespace js {
 
 class ScriptSource;
 class SPSProfiler;
 class AsmJSModule;
 namespace frontend { struct TokenStream; struct ParseNode; }
 namespace ion { class MIRGenerator; class LIRGraph; }
 
@@ -138,36 +134,37 @@ struct DependentAsmJSModuleExit
 // and compilation workers.
 struct AsmJSParallelTask
 {
     LifoAlloc lifo;         // Provider of all heap memory used for compilation.
 
     uint32_t funcNum;       // Index |i| of function in |Module.function(i)|.
     ion::MIRGenerator *mir; // Passed from main thread to worker.
     ion::LIRGraph *lir;     // Passed from worker to main thread.
+    unsigned compileTime;
 
     AsmJSParallelTask(size_t defaultChunkSize)
       : lifo(defaultChunkSize),
-        funcNum(0), mir(NULL), lir(NULL)
+        funcNum(0), mir(NULL), lir(NULL), compileTime(0)
     { }
 
     void init(uint32_t newFuncNum, ion::MIRGenerator *newMir) {
         funcNum = newFuncNum;
         mir = newMir;
         lir = NULL;
     }
 };
 
 // Returns true if the given native is the one that is used to implement asm.js
 // module functions.
-#ifdef JS_ASMJS
-bool
+#ifdef JS_ION
+extern bool
 IsAsmJSModuleNative(js::Native native);
 #else
-static inline bool
+inline bool
 IsAsmJSModuleNative(js::Native native)
 {
     return false;
 }
 #endif
 
 // Exposed for shell testing:
 
--- a/js/src/ion/AsmJSLink.cpp
+++ b/js/src/ion/AsmJSLink.cpp
@@ -18,18 +18,16 @@
 #ifdef MOZ_VTUNE
 # include "jitprofiling.h"
 #endif
 
 using namespace js;
 using namespace js::ion;
 using namespace mozilla;
 
-#ifdef JS_ASMJS
-
 static bool
 LinkFail(JSContext *cx, const char *str)
 {
     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage,
                                  NULL, JSMSG_USE_ASM_LINK_FAIL, str);
     return false;
 }
 
@@ -386,17 +384,18 @@ HandleDynamicLinkFailure(JSContext *cx, 
 
     uint32_t length = info.bufEnd_ - info.bufStart_;
     Rooted<JSStableString*> src(cx, info.scriptSource_->substring(cx, info.bufStart_, info.bufEnd_));
     if (!src)
         return false;
     const jschar *chars = src->chars().get();
 
     RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED,
-                                       cx->global(), name));
+                                       cx->global(), name, JSFunction::FinalizeKind,
+                                       TenuredObject));
     if (!fun)
         return false;
 
     AutoNameVector formals(cx);
     formals.reserve(3);
     if (module.globalArgumentName())
         formals.infallibleAppend(module.globalArgumentName());
     if (module.importArgumentName())
@@ -524,10 +523,8 @@ js::LinkAsmJS(JSContext *cx, unsigned ar
     return true;
 }
 
 bool
 js::IsAsmJSModuleNative(js::Native native)
 {
     return native == LinkAsmJS;
 }
-
-#endif  // defined(JS_ASMJS)
--- a/js/src/ion/AsmJSSignalHandlers.cpp
+++ b/js/src/ion/AsmJSSignalHandlers.cpp
@@ -11,18 +11,16 @@
 #include "ion/AsmJS.h"
 #include "ion/AsmJSModule.h"
 #include "assembler/assembler/MacroAssembler.h"
 
 using namespace js;
 using namespace js::ion;
 using namespace mozilla;
 
-#ifdef JS_ASMJS
-
 #if defined(XP_WIN)
 # define XMM_sig(p,i) ((p)->Xmm##i)
 # define EIP_sig(p) ((p)->Eip)
 # define RIP_sig(p) ((p)->Rip)
 # define RAX_sig(p) ((p)->Rax)
 # define RCX_sig(p) ((p)->Rcx)
 # define RDX_sig(p) ((p)->Rdx)
 # define RBX_sig(p) ((p)->Rbx)
@@ -930,48 +928,45 @@ AsmJSFaultHandler(int signum, siginfo_t 
     } else if (prevHandler->sa_handler == SIG_DFL || prevHandler->sa_handler == SIG_IGN) {
         sigaction(signum, prevHandler, NULL);
     } else {
         prevHandler->sa_handler(signum);
         exit(signum);  // backstop
     }
 }
 # endif
-#endif // JS_ASMJS
 
 bool
 EnsureAsmJSSignalHandlersInstalled(JSRuntime *rt)
 {
-#if defined(JS_ASMJS)
-# if defined(XP_MACOSX)
+#if defined(XP_MACOSX)
     // On OSX, each JSRuntime gets its own handler.
     return rt->asmJSMachExceptionHandler.installed() || rt->asmJSMachExceptionHandler.install(rt);
-# else
+#else
     // Assume Windows or Unix. For these platforms, there is a single,
     // process-wide signal handler installed. Take care to only install it once.
     InstallSignalHandlersMutex::Lock lock;
     if (lock.handlersInstalled())
         return true;
 
-#  if defined(XP_WIN)
+# if defined(XP_WIN)
     if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler))
         return false;
-#  else  // assume Unix
+# else  // assume Unix
     struct sigaction sigAction;
     sigAction.sa_sigaction = &AsmJSFaultHandler;
     sigemptyset(&sigAction.sa_mask);
     sigAction.sa_flags = SA_SIGINFO;
     if (sigaction(SIGSEGV, &sigAction, &sPrevSegvHandler))
         return false;
     if (sigaction(SIGBUS, &sigAction, &sPrevBusHandler))
         return false;
-#  endif
+# endif
 
     lock.setHandlersInstalled();
-# endif
 #endif
     return true;
 }
 
 // To interrupt execution of a JSRuntime, any thread may call
 // JS_TriggerOperationCallback (JSRuntime::triggerOperationCallback from inside
 // the engine). Normally, this sets some state that is polled at regular
 // intervals (function prologues, loop headers), even from jit-code. For tight
@@ -979,33 +974,31 @@ EnsureAsmJSSignalHandlersInstalled(JSRun
 // another thread triggers the operation callback, we simply mprotect all of
 // the innermost asm.js module activation's code. This will trigger a SIGSEGV,
 // taking us into AsmJSFaultHandler. From there, we can manually redirect
 // execution to call js_HandleExecutionInterrupt. The memory is un-protected
 // from the signal handler after control flow is redirected.
 void
 js::TriggerOperationCallbackForAsmJSCode(JSRuntime *rt)
 {
-#if defined(JS_ASMJS)
     JS_ASSERT(rt->currentThreadOwnsOperationCallbackLock());
 
     AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread();
     if (!activation)
         return;
 
     const AsmJSModule &module = activation->module();
 
-# if defined(XP_WIN)
+#if defined(XP_WIN)
     DWORD oldProtect;
     if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_NOACCESS, &oldProtect))
         MOZ_CRASH();
-# else  // assume Unix
+#else  // assume Unix
     if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE))
         MOZ_CRASH();
-# endif
 #endif
 }
 
 #ifdef MOZ_ASAN
 // When running with asm.js under AddressSanitizer, we need to explicitely
 // tell AddressSanitizer to allow custom signal handlers because it will 
 // otherwise trigger ASan's SIGSEGV handler for the internal SIGSEGVs that 
 // asm.js would otherwise handle.
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -5183,37 +5183,16 @@ CodeGenerator::generate()
     if (!generateInvalidateEpilogue())
         return false;
     if (!generateOutOfLineCode())
         return false;
 
     return !masm.oom();
 }
 
-#ifdef JSGC_GENERATIONAL
-/*
- * IonScripts normally live as long as their owner JSScript; however, they can
- * occasionally get destroyed outside the context of a GC by FinishInvalidationOf.
- * Because of this case, we cannot use the normal store buffer to guard them.
- * Instead we use the generic buffer to mark the owner script, which will mark the
- * IonScript's fields, if it is still alive.
- */
-class IonScriptRefs : public gc::BufferableRef
-{
-    JSScript *script_;
-
-  public:
-    IonScriptRefs(JSScript *script) : script_(script) {}
-    virtual bool match(void *location) { return false; }
-    virtual void mark(JSTracer *trc) {
-        gc::MarkScriptUnbarriered(trc, &script_, "script for IonScript");
-    }
-};
-#endif // JSGC_GENERATIONAL
-
 bool
 CodeGenerator::link()
 {
     JSContext *cx = GetIonContext()->cx;
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::ION_CODE);
     if (!code)
@@ -5301,19 +5280,16 @@ CodeGenerator::link()
     if (bailouts_.length())
         ionScript->copyBailoutTable(&bailouts_[0]);
     if (osiIndices_.length())
         ionScript->copyOsiIndices(&osiIndices_[0], masm);
     if (snapshots_.size())
         ionScript->copySnapshots(&snapshots_);
     if (graph.numConstants())
         ionScript->copyConstants(graph.constantPool());
-#ifdef JSGC_GENERATIONAL
-    cx->runtime()->gcStoreBuffer.putGeneric(IonScriptRefs(script));
-#endif
     JS_ASSERT(graph.mir().numScripts() > 0);
     ionScript->copyScriptEntries(graph.mir().scripts());
     if (callTargets.length() > 0)
         ionScript->copyCallTargetEntries(callTargets.begin());
 
     // The correct state for prebarriers is unknown until the end of compilation,
     // since a GC can occur during code generation. All barriers are emitted
     // off-by-default, and are toggled on here if necessary.
--- a/js/src/ion/CompileInfo-inl.h
+++ b/js/src/ion/CompileInfo-inl.h
@@ -3,21 +3,44 @@
  * 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/. */
 
 #ifndef jsion_compileinfo_inl_h__
 #define jsion_compileinfo_inl_h__
 
 #include "CompileInfo.h"
+#include "jsgcinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace ion;
 
+CompileInfo::CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
+                         ExecutionMode executionMode)
+  : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
+    executionMode_(executionMode)
+{
+    JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
+
+    // The function here can flow in from anywhere so look up the canonical function to ensure that
+    // we do not try to embed a nursery pointer in jit-code.
+    if (fun_) {
+        fun_ = fun_->nonLazyScript()->function();
+        JS_ASSERT(fun_->isTenured());
+    }
+
+    nimplicit_ = StartArgSlot(script, fun)              /* scope chain and argument obj */
+               + (fun ? 1 : 0);                         /* this */
+    nargs_ = fun ? fun->nargs : 0;
+    nlocals_ = script->nfixed;
+    nstack_ = script->nslots - script->nfixed;
+    nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
+}
+
 const char *
 CompileInfo::filename() const
 {
     return script_->filename();
 }
 
 JSAtom *
 CompileInfo::getAtom(jsbytecode *pc) const
--- a/js/src/ion/CompileInfo.h
+++ b/js/src/ion/CompileInfo.h
@@ -35,28 +35,17 @@ enum ExecutionMode {
     ParallelExecution
 };
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
     CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing,
-                ExecutionMode executionMode)
-      : script_(script), fun_(fun), osrPc_(osrPc), constructing_(constructing),
-        executionMode_(executionMode)
-    {
-        JS_ASSERT_IF(osrPc, JSOp(*osrPc) == JSOP_LOOPENTRY);
-        nimplicit_ = StartArgSlot(script, fun)              /* scope chain and argument obj */
-                   + (fun ? 1 : 0);                         /* this */
-        nargs_ = fun ? fun->nargs : 0;
-        nlocals_ = script->nfixed;
-        nstack_ = script->nslots - script->nfixed;
-        nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
-    }
+                ExecutionMode executionMode);
 
     CompileInfo(unsigned nlocals, ExecutionMode executionMode)
       : script_(NULL), fun_(NULL), osrPc_(NULL), constructing_(false),
         executionMode_(executionMode)
     {
         nimplicit_ = 0;
         nargs_ = 0;
         nlocals_ = nlocals;
--- a/js/src/ion/EffectiveAddressAnalysis.cpp
+++ b/js/src/ion/EffectiveAddressAnalysis.cpp
@@ -4,17 +4,16 @@
  * 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 "EffectiveAddressAnalysis.h"
 
 using namespace js;
 using namespace ion;
 
-#ifdef JS_ASMJS
 static void
 AnalyzeLsh(MBasicBlock *block, MLsh *lsh)
 {
     if (lsh->specialization() != MIRType_Int32)
         return;
 
     MDefinition *index = lsh->lhs();
     JS_ASSERT(index->type() == MIRType_Int32);
@@ -82,17 +81,16 @@ AnalyzeLsh(MBasicBlock *block, MLsh *lsh
         bitAnd->replaceAllUsesWith(last);
         return;
     }
 
     MEffectiveAddress *eaddr = MEffectiveAddress::New(base, index, scale, displacement);
     last->replaceAllUsesWith(eaddr);
     block->insertAfter(last, eaddr);
 }
-#endif
 
 // This analysis converts patterns of the form:
 //   truncate(x + (y << {0,1,2,3}))
 //   truncate(x + (y << {0,1,2,3}) + imm32)
 // into a single lea instruction, and patterns of the form:
 //   asmload(x + imm32)
 //   asmload(x << {0,1,2,3})
 //   asmload((x << {0,1,2,3}) + imm32)
@@ -101,18 +99,16 @@ AnalyzeLsh(MBasicBlock *block, MLsh *lsh
 // into a single asmload instruction (and for asmstore too).
 //
 // Additionally, we should consider the general forms:
 //   truncate(x + y + imm32)
 //   truncate((y << {0,1,2,3}) + imm32)
 bool
 EffectiveAddressAnalysis::analyze()
 {
-#if defined(JS_ASMJS)
     for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) {
         for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
             if (i->isLsh())
                 AnalyzeLsh(*block, i->toLsh());
         }
     }
-#endif
     return true;
 }
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -521,17 +521,17 @@ IonCode::writeBarrierPre(IonCode *code)
     if (zone->needsBarrier())
         MarkIonCodeUnbarriered(zone->barrierTracer(), &code, "ioncode write barrier");
 #endif
 }
 
 void
 IonCode::writeBarrierPost(IonCode *code, void *addr)
 {
-#ifdef JSGC_INCREMENTAL
+#ifdef JSGC_GENERATIONAL
     // Nothing to do.
 #endif
 }
 
 IonScript::IonScript()
   : method_(NULL),
     deoptTable_(NULL),
     osrPc_(NULL),
@@ -1261,18 +1261,17 @@ AttachFinishedCompilations(JSContext *cx
     if (!ion || !cx->runtime()->workerThreadState)
         return;
 
     AutoLockWorkerThreadState lock(cx->runtime());
 
     OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
     // Incorporate any off thread compilations which have finished, failed or
-    // have been cancelled, and destroy JM jitcode for any compilations which
-    // succeeded, to allow entering the Ion code from the interpreter.
+    // have been cancelled.
     while (!compilations.empty()) {
         IonBuilder *builder = compilations.popCopy();
 
         if (CodeGenerator *codegen = builder->backgroundCodegen()) {
             RootedScript script(cx, builder->script());
             IonContext ictx(cx, &builder->temp());
 
             // Root the assembler until the builder is finished below. As it
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -434,38 +434,40 @@ IsCacheableDOMProxy(JSObject *obj)
 
     if (obj->numFixedSlots() <= GetDOMProxyExpandoSlot())
         return false;
 
     return true;
 }
 
 static void
-GeneratePrototypeGuards(JSContext *cx, MacroAssembler &masm, JSObject *obj, JSObject *holder,
-                        Register objectReg, Register scratchReg, Label *failures)
+GeneratePrototypeGuards(JSContext *cx, IonScript *ion, MacroAssembler &masm, JSObject *obj,
+                        JSObject *holder, Register objectReg, Register scratchReg,
+                        Label *failures)
 {
     JS_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // Note: objectReg and scratchReg may be the same register, so we cannot
         // use objectReg in the rest of this function.
         masm.loadPtr(Address(objectReg, JSObject::offsetOfType()), scratchReg);
         Address proto(scratchReg, offsetof(types::TypeObject, proto));
-        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures);
+        masm.branchPtr(Assembler::NotEqual, proto,
+                       ImmMaybeNurseryPtr(ion->method(), obj->getProto()), failures);
     }
 
     JSObject *pobj = IsCacheableDOMProxy(obj)
                      ? obj->getTaggedProto().toObjectOrNull()
                      : obj->getProto();
     if (!pobj)
         return;
     while (pobj != holder) {
         if (pobj->hasUncacheableProto()) {
             JS_ASSERT(!pobj->hasSingletonType());
-            masm.movePtr(ImmGCPtr(pobj), scratchReg);
+            masm.movePtr(ImmMaybeNurseryPtr(ion->method(), pobj), scratchReg);
             Address objType(scratchReg, JSObject::offsetOfType());
             masm.branchPtr(Assembler::NotEqual, objType, ImmGCPtr(pobj->type()), failures);
         }
         pobj = pobj->getProto();
     }
 }
 
 static bool
@@ -694,19 +696,20 @@ GenerateDOMProxyChecks(JSContext *cx, Ma
     masm.jump(stubFailure);
 
     // Success case: restore the tempval and proceed.
     masm.bind(&domProxyOk);
     masm.popValue(tempVal);
 }
 
 static void
-GenerateReadSlot(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                 JSObject *obj, PropertyName *name, JSObject *holder, Shape *shape,
-                 Register object, TypedOrValueRegister output, Label *failures = NULL)
+GenerateReadSlot(JSContext *cx, IonScript *ion, MacroAssembler &masm,
+                 IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
+                 JSObject *holder, Shape *shape, Register object, TypedOrValueRegister output,
+                 Label *failures = NULL)
 {
     // If there's a single jump to |failures|, we can patch the shape guard
     // jump directly. Otherwise, jump to the end of the stub, so there's a
     // common point to patch.
     bool multipleFailureJumps = (obj != holder) || (failures != NULL && failures->used());
 
     // If we have multiple failure jumps but didn't get a label from the
     // outside, make one ourselves.
@@ -754,23 +757,23 @@ GenerateReadSlot(JSContext *cx, MacroAss
         return;
     }
 
     // Slow path: multiple jumps; generate prototype guards.
     Label prototypeFailures;
     Register holderReg;
     if (obj != holder) {
         // Note: this may clobber the object register if it's used as scratch.
-        GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg,
+        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
                                 failures);
 
         if (holder) {
             // Guard on the holder's shape.
             holderReg = scratchReg;
-            masm.movePtr(ImmGCPtr(holder), holderReg);
+            masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), holderReg);
             masm.branchPtr(Assembler::NotEqual,
                            Address(holderReg, JSObject::offsetOfShape()),
                            ImmGCPtr(holder->lastProperty()),
                            &prototypeFailures);
         } else {
             // The property does not exist. Guard on everything in the
             // prototype chain.
             JSObject *proto = obj->getTaggedProto().toObjectOrNull();
@@ -822,39 +825,39 @@ GenerateReadSlot(JSContext *cx, MacroAss
 
     attacher.jumpNextStub(masm);
 
     if (restoreScratch)
         masm.pop(scratchReg);
 }
 
 static bool
-GenerateCallGetter(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
-                   JSObject *obj, PropertyName *name, JSObject *holder, HandleShape shape,
-                   RegisterSet &liveRegs, Register object, TypedOrValueRegister output,
-                   void *returnAddr, jsbytecode *pc)
+GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
+                   IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
+                   JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
+                   TypedOrValueRegister output, void *returnAddr, jsbytecode *pc)
 {
     // Initial shape check.
     Label stubFailure;
     masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
                    ImmGCPtr(obj->lastProperty()), &stubFailure);
 
     if (IsCacheableDOMProxy(obj))
         GenerateDOMProxyChecks(cx, masm, obj, name, object, &stubFailure);
 
     JS_ASSERT(output.hasValue());
     Register scratchReg = output.valueReg().scratchReg();
 
     // Note: this may clobber the object register if it's used as scratch.
     if (obj != holder)
-        GeneratePrototypeGuards(cx, masm, obj, holder, object, scratchReg, &stubFailure);
+        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &stubFailure);
 
     // Guard on the holder's shape.
     Register holderReg = scratchReg;
-    masm.movePtr(ImmGCPtr(holder), holderReg);
+    masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), holderReg);
     masm.branchPtr(Assembler::NotEqual,
                    Address(holderReg, JSObject::offsetOfShape()),
                    ImmGCPtr(holder->lastProperty()),
                    &stubFailure);
 
     // Now we're good to go to invoke the native call.
 
     // saveLive()
@@ -1008,17 +1011,17 @@ GenerateCallGetter(JSContext *cx, MacroA
 }
 
 bool
 GetPropertyIC::attachReadSlot(JSContext *cx, IonScript *ion, JSObject *obj, JSObject *holder,
                               HandleShape shape)
 {
     RepatchStubAppender attacher(*this);
     MacroAssembler masm(cx);
-    GenerateReadSlot(cx, masm, attacher, obj, name(), holder, shape, object(), output());
+    GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
     const char *attachKind = "non idempotent reading";
     if (idempotent())
         attachKind = "idempotent reading";
     return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
 }
 
 bool
 GetPropertyIC::attachDOMProxyShadowed(JSContext *cx, IonScript *ion, JSObject *obj,
@@ -1142,17 +1145,17 @@ GetPropertyIC::attachCallGetter(JSContex
     JS_ASSERT(!idempotent());
     JS_ASSERT(allowGetters());
 
     // Need to set correct framePushed on the masm so that exit frame descriptors are
     // properly constructed.
     masm.setFramePushed(ion->frameSize());
 
     RepatchStubAppender attacher(*this);
-    if (!GenerateCallGetter(cx, masm, attacher, obj, name(), holder, shape, liveRegs_,
+    if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
                             object(), output(), returnAddr, pc))
     {
          return false;
     }
 
     const char *attachKind = "non idempotent calling";
     if (idempotent())
         attachKind = "idempotent calling";
@@ -1616,17 +1619,17 @@ ParallelGetPropertyIC::attachReadSlot(Lo
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!canAttachReadSlot(cx, obj, &holder, &shape))
         return true;
 
     // Ready to generate the read slot stub.
     DispatchStubPrepender attacher(*this);
     MacroAssembler masm(cx);
-    GenerateReadSlot(cx, masm, attacher, obj, name(), holder, shape, object(), output());
+    GenerateReadSlot(cx, ion, masm, attacher, obj, name(), holder, shape, object(), output());
 
     const char *attachKind = "parallel non-idempotent reading";
     if (idempotent())
         attachKind = "parallel idempotent reading";
 
     if (!linkAndAttachStub(cx, masm, attacher, ion, attachKind))
         return false;
 
@@ -1789,19 +1792,19 @@ SetPropertyIC::attachSetterCall(JSContex
         Register scratchReg = regSet.takeGeneral();
         masm.push(scratchReg);
 
         Label protoFailure;
         Label protoSuccess;
 
         // Generate prototype/shape guards.
         if (obj != holder)
-            GeneratePrototypeGuards(cx, masm, obj, holder, object(), scratchReg, &protoFailure);
-
-        masm.movePtr(ImmGCPtr(holder), scratchReg);
+            GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &protoFailure);
+
+        masm.movePtr(ImmMaybeNurseryPtr(ion->method(), holder), scratchReg);
         masm.branchPtr(Assembler::NotEqual,
                        Address(scratchReg, JSObject::offsetOfShape()),
                        ImmGCPtr(holder->lastProperty()),
                        &protoFailure);
 
         masm.jump(&protoSuccess);
 
         masm.bind(&protoFailure);
@@ -2180,17 +2183,17 @@ GetElementIC::attachGetProp(JSContext *c
     Label failures;
     MacroAssembler masm(cx);
 
     // Guard on the index value.
     ValueOperand val = index().reg().valueReg();
     masm.branchTestValue(Assembler::NotEqual, val, idval, &failures);
 
     RepatchStubAppender attacher(*this);
-    GenerateReadSlot(cx, masm, attacher, obj, name, holder, shape, object(), output(),
+    GenerateReadSlot(cx, ion, masm, attacher, obj, name, holder, shape, object(), output(),
                      &failures);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "property");
 }
 
 bool
 GetElementIC::attachDenseElement(JSContext *cx, IonScript *ion, JSObject *obj, const Value &idval)
 {
@@ -2949,17 +2952,17 @@ NameIC::attachCallGetter(JSContext *cx, 
 {
     MacroAssembler masm(cx);
 
     // Need to set correct framePushed on the masm so that exit frame descriptors are
     // properly constructed.
     masm.setFramePushed(ion->frameSize());
 
     RepatchStubAppender attacher(*this);
-    if (!GenerateCallGetter(cx, masm, attacher, obj, name(), holder, shape, liveRegs_,
+    if (!GenerateCallGetter(cx, ion, masm, attacher, obj, name(), holder, shape, liveRegs_,
                             scopeChainReg(), outputReg(), returnAddr, pc))
     {
          return false;
     }
 
     const char *attachKind = "name getter";
     return linkAndAttachStub(cx, masm, attacher, ion, attachKind);
 }
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -1203,17 +1203,16 @@ MacroAssembler::popRooted(VMFunction::Ro
         break;
       case VMFunction::RootValue:
         loadValue(Address(StackPointer, 0), valueReg);
         freeStack(sizeof(Value));
         break;
     }
 }
 
-#ifdef JS_ASMJS
 ABIArgIter::ABIArgIter(const MIRTypeVector &types)
   : gen_(),
     types_(types),
     i_(0)
 {
     if (!done())
         gen_.next(types_[i_]);
 }
@@ -1221,9 +1220,8 @@ ABIArgIter::ABIArgIter(const MIRTypeVect
 void
 ABIArgIter::operator++(int)
 {
     JS_ASSERT(!done());
     i_++;
     if (!done())
         gen_.next(types_[i_]);
 }
-#endif
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -968,17 +968,16 @@ JSOpToCondition(JSOp op, bool isSigned)
             JS_NOT_REACHED("Unrecognized comparison operation");
             return Assembler::Equal;
         }
     }
 }
 
 typedef Vector<MIRType, 8> MIRTypeVector;
 
-#ifdef JS_ASMJS
 class ABIArgIter
 {
     ABIArgGenerator gen_;
     const MIRTypeVector &types_;
     unsigned i_;
 
   public:
     ABIArgIter(const MIRTypeVector &argTypes);
@@ -988,14 +987,13 @@ class ABIArgIter
 
     ABIArg *operator->() { JS_ASSERT(!done()); return &gen_.current(); }
     ABIArg &operator*() { JS_ASSERT(!done()); return gen_.current(); }
 
     unsigned index() const { JS_ASSERT(!done()); return i_; }
     MIRType mirType() const { JS_ASSERT(!done()); return types_[i_]; }
     uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); }
 };
-#endif
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_macro_assembler_h__
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -571,17 +571,17 @@ void
 PostWriteBarrier(JSRuntime *rt, JSObject *obj)
 {
 #ifdef JS_GC_ZEAL
     /* The jitcode version of IsInsideNursery does not know about the verifier. */
     if (rt->gcVerifyPostData && rt->gcVerifierNursery.isInside(obj))
         return;
 #endif
     JS_ASSERT(!IsInsideNursery(rt, obj));
-    rt->gcStoreBuffer.putWholeObject(obj);
+    rt->gcStoreBuffer.putWholeCell(obj);
 }
 #endif
 
 uint32_t
 GetIndexFromString(JSString *str)
 {
     // Masks the return value UINT32_MAX as failure to get the index.
     // I.e. it is impossible to distinguish between failing to get the index
--- a/js/src/ion/shared/Assembler-shared.h
+++ b/js/src/ion/shared/Assembler-shared.h
@@ -116,16 +116,34 @@ struct ImmWord
 // Used for immediates which require relocation.
 struct ImmGCPtr
 {
     uintptr_t value;
 
     explicit ImmGCPtr(const gc::Cell *ptr) : value(reinterpret_cast<uintptr_t>(ptr))
     {
         JS_ASSERT(!IsPoisonedPtr(ptr));
+        JS_ASSERT_IF(ptr, ptr->isTenured());
+    }
+
+  protected:
+    ImmGCPtr() : value(0) {}
+};
+
+// Used for immediates which require relocation and may be traced during minor GC.
+struct ImmMaybeNurseryPtr : public ImmGCPtr
+{
+    explicit ImmMaybeNurseryPtr(IonCode *code, gc::Cell *ptr)
+    {
+        this->value = reinterpret_cast<uintptr_t>(ptr);
+        JS_ASSERT(!IsPoisonedPtr(ptr));
+#ifdef JSGC_GENERATIONAL
+        if (ptr && ptr->runtime()->gcNursery.isInside(ptr))
+            ptr->runtime()->gcStoreBuffer.putWholeCell(code);
+#endif
     }
 };
 
 // Specifies a hardcoded, absolute address.
 struct AbsoluteAddress {
     void *addr;
 
     explicit AbsoluteAddress(void *addr)
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -30,18 +30,20 @@ class AssemblerX86Shared
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
     Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
     CompactBufferWriter preBarriers_;
     bool enoughMemory_;
 
     void writeDataRelocation(const Value &val) {
-        if (val.isMarkable())
+        if (val.isMarkable()) {
+            JS_ASSERT(static_cast<gc::Cell*>(val.toGCThing())->isTenured());
             dataRelocations_.writeUnsigned(masm.currentOffset());
+        }
     }
     void writeDataRelocation(const ImmGCPtr &ptr) {
         if (ptr.value)
             dataRelocations_.writeUnsigned(masm.currentOffset());
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         preBarriers_.writeUnsigned(label.offset());
     }
--- a/js/src/ion/x86/CodeGenerator-x86.cpp
+++ b/js/src/ion/x86/CodeGenerator-x86.cpp
@@ -498,19 +498,19 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     return gen->noteHeapAccess(AsmJSHeapAccess(cmp.offset(), before, after, vt, ToAnyRegister(out)));
 }
 
 bool
 CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
-    if (ool->dest().isFloat())
+    if (ool->dest().isFloat()) {
         masm.movsd(&js_NaN, ool->dest().fpu());
-    else {
+    } else {
         Register destReg = ool->dest().gpr();
         masm.xorl(destReg, destReg);
     }
     masm.jmp(ool->rejoin());
     return true;
 }
 
 void
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -18,16 +18,17 @@ assertAsmTypeFail(USE_ASM + 'function f(
 assertAsmTypeFail(USE_ASM + 'function f(){return; return 1} return f');
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0} return f'))(42), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0; return x|0} return f'))(42), 42);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0; return x|0;;;} return f'))(42), 42);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x,y){x=x|0;y=y|0; return (x+y)|0} return f'))(44, -2), 42);
 assertAsmTypeFail('a', USE_ASM + 'function a(){} return a');
 assertAsmTypeFail('a','b','c', USE_ASM + 'var c');
 assertAsmTypeFail('a','b','c', USE_ASM + 'var c=0');
+assertAsmTypeFail(USE_ASM + 'c=0;return {}');
 assertAsmTypeFail('x','x', USE_ASM + 'function a(){} return a');
 assertAsmTypeFail('x','y','x', USE_ASM + 'function a(){} return a');
 assertEq(asmLink(asmCompile('x', USE_ASM + 'function a(){} return a'))(), undefined);
 assertEq(asmLink(asmCompile('x','y', USE_ASM + 'function a(){} return a'))(), undefined);
 assertEq(asmLink(asmCompile('x','y','z', USE_ASM + 'function a(){} return a'))(), undefined);
 assertAsmTypeFail('x','y', USE_ASM + 'function y(){} return f');
 assertEq(asmLink(asmCompile('x', USE_ASM + 'function f(){} return f'), 1, 2, 3)(), undefined);
 assertEq(asmLink(asmCompile('x', USE_ASM + 'function f(){} return f'), 1)(), undefined);
--- a/js/src/jit-test/tests/asm.js/testFFI.js
+++ b/js/src/jit-test/tests/asm.js/testFFI.js
@@ -14,17 +14,17 @@ function add1(x) { return x+1 }
 function add2(x,y) { return x+y }
 function add3(x,y,z) { return x+y+z }
 function addN() {
     var sum = 0;
     for (var i = 0; i < arguments.length; i++)
         sum += arguments[i];
     return sum;
 }
-var imp = { inc:inc, add1:add1, add2:add2, add3:add3, addN:addN };
+var imp = { inc:inc, add1:add1, add2:add2, add3:add3, addN:addN, identity: x => x };
 
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { incc() } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { var i = 0; return (i + inc)|0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { inc = 0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (inc() + 1)|0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +((inc()|0) + 1.1) } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +(inc() + 1.1) } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (+inc() + 1)|0 } return f');
@@ -85,8 +85,10 @@ assertEq(f(2,0), 3);
 assertEq(f(3,0), 4);
 assertEq(f(4,5), 10);
 
 var recurse = function(i,j) { if (i == 0) throw j; f(i-1,j) }
 var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function g(i,j,k) { i=i|0;j=+j;k=k|0; if (!(k|0)) ffi(i|0,j)|0; else g(i, j+1.0, (k-1)|0) } function f(i,j) { i=i|0;j=+j; g(i,j,4) } return f'), null, {ffi:recurse});
 assertThrowsValue(function() { f(0,2.4) }, 2.4+4);
 assertThrowsValue(function() { f(1,2.4) }, 2.4+8);
 assertThrowsValue(function() { f(8,2.4) }, 2.4+36);
+
+assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var identity=imp.identity; function g(x) { x=+x; return +identity(x) } return g'), null, imp)(13.37), 13.37);
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -1,10 +1,13 @@
 load(libdir + "asm.js");
 
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[0>>2] = 4.0; return i32[0>>2]|0; } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[0>>2] = 4; return +f32[0>>2]; } return f');
+
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { var x=0,y=0; return i8[x+y]|0 } return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { var x=0,y=0; return u8[x+y]|0 } return f');
 
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>0]|0 }; return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>1]|0 }; return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0>>4]|0 }; return f');
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[0]|0 }; return f'), this, null, new ArrayBuffer(4096))(), 0);
 
@@ -156,8 +159,11 @@ asmCompile('glob', 'imp', 'b', USE_ASM +
 var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[64] } return f');
 asmLink(code, this, null, new ArrayBuffer(4096));
 
 asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[12] = i } return f'), this, null, BUF_64KB)(11);
 assertEq(new Int32Array(BUF_64KB)[12], 11);
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[12]|0 } return f'), this, null, BUF_64KB)(), 11);
 new Float64Array(BUF_64KB)[0] = 3.5;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +-f64[0] } return f'), this, null, BUF_64KB)(), -3.5);
+
+// Bug 882012
+assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var identity=foreign.identity;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=identity(2.0);return +doubles[0];}return g"), this, {identity: function(x){return x;}}, BUF_64KB)(), 2.0);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-880816.js
@@ -0,0 +1,30 @@
+var lfcode = new Array();
+lfcode.push("const baz = 'bar';");
+lfcode.push("2");
+lfcode.push("{ function foo() {} }");
+lfcode.push("evaluate('\
+var INVALIDATE_MODES = INVALIDATE_MODE_STRINGS.map(s => ({mode: s}));\
+function range(n, m) {}\
+function seq_scan(array, f) {}\
+function assertStructuralEq(e1, e2) {}\
+for (var i = 0, l = a.length; i < l; i++) {}\
+');");
+lfcode.push("for (var x of Set(Object.getOwnPropertyNames(this))) {}");
+var lfRunTypeId = -1;
+while (true) {
+  var file = lfcode.shift(); if (file == undefined) { break; }
+  loadFile(file)
+}
+function loadFile(lfVarx) {
+    try {
+        if (lfVarx.substr(-3) == ".js") {}
+        if (!isNaN(lfVarx)) {
+            lfRunTypeId = parseInt(lfVarx);
+        } else {
+            switch (lfRunTypeId) {
+                case 2: new Function(lfVarx)(); break;
+                default: evaluate(lfVarx); break;
+            }
+       }
+    } catch (lfVare) {}
+}
--- a/js/src/js-config.in
+++ b/js/src/js-config.in
@@ -104,14 +104,18 @@ if test "$echo_includedir" = "yes"; then
     echo $includedir
 fi
 
 if test "$echo_libdir" = "yes"; then
     echo $libdir
 fi
 
 if test "$echo_cflags" = "yes"; then
-    echo "-include $includedir/$LIBRARY_NAME/js/RequiredDefines.h -I$includedir/$LIBRARY_NAME $NSPR_CFLAGS"
+    cflags="-include $includedir/$LIBRARY_NAME/js/RequiredDefines.h -I$includedir/$LIBRARY_NAME $NSPR_CFLAGS"
+    if test '@USE_CXX11@' = "1"; then
+        cflags="$cflags -std=gnu++0x"
+    fi
+    echo $cflags
 fi
 
 if test "$echo_libs" = "yes"; then
     echo "$MOZ_JS_LIBS $JS_CONFIG_LIBS"
 fi
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -389,15 +389,15 @@ MSG_DEF(JSMSG_UNDEFINED_CURRENCY,     33
 MSG_DEF(JSMSG_INVALID_TIME_ZONE,      336, 1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
 MSG_DEF(JSMSG_DATE_NOT_FINITE,        337, 0, JSEXN_RANGEERR, "date value is not finite in DateTimeFormat.format()")
 MSG_DEF(JSMSG_MODULE_STATEMENT,       338, 0, JSEXN_SYNTAXERR, "module declarations may only appear at the top level of a program or module body")
 MSG_DEF(JSMSG_CURLY_BEFORE_MODULE,    339, 0, JSEXN_SYNTAXERR, "missing { before module body")
 MSG_DEF(JSMSG_CURLY_AFTER_MODULE,     340, 0, JSEXN_SYNTAXERR, "missing } after module body")
 MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 341, 0, JSEXN_SYNTAXERR, "'use asm' directive only works on function code")
 MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,      342, 1, JSEXN_TYPEERR, "asm.js type error: {0}")
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,      343, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
-MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 0, JSEXN_ERR,     "successfully compiled asm.js code")
+MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 1, JSEXN_ERR,     "successfully compiled asm.js code ({0})")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,         345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_YIELD_IN_ARROW,         346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
 MSG_DEF(JSMSG_WRONG_VALUE,            347, 2, JSEXN_ERR, "expected {0} but found {1}")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
 MSG_DEF(JSMSG_DEPRECATED_SOURCE_MAP,  350, 0, JSEXN_SYNTAXERR, "Using //@ to indicate source map URL pragmas is deprecated. Use //# instead")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5494,17 +5494,18 @@ JS::CompileFunction(JSContext *cx, Handl
 
     AutoNameVector formals(cx);
     for (unsigned i = 0; i < nargs; i++) {
         RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i])));
         if (!argAtom || !formals.append(argAtom->asPropertyName()))
             return NULL;
     }
 
-    RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, obj, funAtom));
+    RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, obj,
+                                       funAtom, JSFunction::FinalizeKind, TenuredObject));
     if (!fun)
         return NULL;
 
     if (!frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length))
         return NULL;
 
     if (obj && funAtom) {
         Rooted<jsid> id(cx, AtomToId(funAtom));
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -24,17 +24,17 @@
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "frontend/SourceNotes.h"
 #include "vm/Debugger.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 
-#ifdef JS_ASMJS
+#ifdef JS_ION
 #include "ion/AsmJSModule.h"
 #endif
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
@@ -907,17 +907,17 @@ JS_DumpCompartmentPCCounts(JSContext *cx
         JSScript *script = i.get<JSScript>();
         if (script->compartment() != cx->compartment())
             continue;
 
         if (script->hasScriptCounts && script->enclosingScriptsCompiledSuccessfully())
             JS_DumpPCCounts(cx, script);
     }
 
-#if defined(JS_ASMJS) && defined(DEBUG)
+#if defined(JS_ION) && defined(DEBUG)
     for (unsigned thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
         for (CellIter i(cx->zone(), (AllocKind) thingKind); !i.done(); i.next()) {
             JSObject *obj = i.get<JSObject>();
             if (obj->compartment() != cx->compartment())
                 continue;
 
             if (IsAsmJSModuleObject(obj)) {
                 AsmJSModule &module = AsmJSModuleObjectToModule(obj);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -324,17 +324,17 @@ fun_resolve(JSContext *cx, HandleObject 
     return true;
 }
 
 template<XDRMode mode>
 bool
 js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
                            MutableHandleObject objp)
 {
-    /* NB: Keep this in sync with CloneInterpretedFunction. */
+    /* NB: Keep this in sync with CloneFunctionAndScript. */
     RootedAtom atom(xdr->cx());
     uint32_t firstword;           /* flag telling whether fun->atom is non-null,
                                    plus for fun->u.i.skipmin, fun->u.i.wrapper,
                                    and 14 bits reserved for future use */
     uint32_t flagsword;           /* word for argument count and fun->flags */
 
     JSContext *cx = xdr->cx();
     RootedFunction fun(cx);
@@ -349,17 +349,18 @@ js::XDRInterpretedFunction(XDRState<mode
             }
             return false;
         }
         firstword = !!fun->atom();
         flagsword = (fun->nargs << 16) | fun->flags;
         atom = fun->atom();
         script = fun->nonLazyScript();
     } else {
-        fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr());
+        fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(),
+                          JSFunction::FinalizeKind, TenuredObject);
         if (!fun)
             return false;
         atom = NULL;
         script = NULL;
     }
 
     if (!xdr->codeUint32(&firstword))
         return false;
@@ -390,24 +391,23 @@ js::XDRInterpretedFunction(XDRState<mode
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, MutableHandleObject);
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, MutableHandleObject);
 
 JSObject *
-js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun,
-                             NewObjectKind newKind /* = GenericObject */)
+js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
 
     RootedFunction clone(cx, NewFunction(cx, NullPtr(), NULL, 0,
                                          JSFunction::INTERPRETED, NullPtr(), NullPtr(),
-                                         JSFunction::FinalizeKind, newKind));
+                                         JSFunction::FinalizeKind, TenuredObject));
     if (!clone)
         return NULL;
 
     RootedScript srcScript(cx, srcFun->nonLazyScript());
     RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript));
     if (!clonedScript)
         return NULL;
 
@@ -1457,17 +1457,18 @@ js::Function(JSContext *cx, unsigned arg
     /*
      * NB: (new Function) is not lexically closed by its caller, it's just an
      * anonymous function in the top-level scope that its constructor inhabits.
      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
      * and so would a call to f from another top-level's script or function.
      */
     RootedAtom anonymousAtom(cx, cx->names().anonymous);
     RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED_LAMBDA,
-                                       global, anonymousAtom));
+                                       global, anonymousAtom, JSFunction::FinalizeKind,
+                                       TenuredObject));
     if (!fun)
         return false;
 
     if (hasRest)
         fun->setHasRest();
 
     bool ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length);
     args.rval().setObject(*fun);
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -438,18 +438,17 @@ namespace js {
 JSString *FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen);
 
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleObject objp);
 
 extern JSObject *
-CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
-                         NewObjectKind newKind = GenericObject);
+CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
 
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
 ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -651,23 +651,23 @@ JSObject::parExtendDenseElements(js::All
     }
 
     // Watch out lest the header has been reallocated by
     // extendDenseElements():
     header = getElementsHeader();
 
     js::HeapSlot *sp = elements + initializedLength;
     if (v) {
-        for (uint32_t i = 0; i < extra; i++)
-            sp[i].init(runtime(), this, js::HeapSlot::Element, initializedLength+i, v[i]);
+        for (uint32_t i = 0; i < extra; i++) {
+            JS_ASSERT_IF(v[i].isMarkable(), static_cast<js::gc::Cell *>(v[i].toGCThing())->isTenured());
+            *sp[i].unsafeGet() = v[i];
+        }
     } else {
-        for (uint32_t i = 0; i < extra; i++) {
-            sp[i].init(runtime(), this, js::HeapSlot::Element,
-                       initializedLength + i, js::MagicValue(JS_ELEMENTS_HOLE));
-        }
+        for (uint32_t i = 0; i < extra; i++)
+            *sp[i].unsafeGet() = js::MagicValue(JS_ELEMENTS_HOLE);
     }
     header->initializedLength = requiredCapacity;
     if (header->length < requiredCapacity)
         header->length = requiredCapacity;
     return ED_OK;
 }
 
 inline JSObject::EnsureDenseResult
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2329,18 +2329,17 @@ js::CloneScript(JSContext *cx, HandleObj
                     RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
                     StaticScopeIter ssi(cx, staticScope);
                     RootedObject enclosingScope(cx);
                     if (!ssi.done() && ssi.type() == StaticScopeIter::BLOCK)
                         enclosingScope = objects[FindBlockIndex(src, ssi.block())];
                     else
                         enclosingScope = fun;
 
-                    clone = CloneInterpretedFunction(cx, enclosingScope, innerFun,
-                            src->selfHosted ? TenuredObject : newKind);
+                    clone = CloneFunctionAndScript(cx, enclosingScope, innerFun);
                 }
             } else {
                 /*
                  * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that
                  * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled
                  * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this
                  * case should only ever be hit when cloning objects from self-hosted code.
                  */
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -82,16 +82,17 @@ void
 SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
                         HandleScript script, JSObject *argsobj);
 
 } // namespace js
 
 inline void
 JSScript::setFunction(JSFunction *fun)
 {
+    JS_ASSERT(fun->isTenured());
     function_ = fun;
 }
 
 inline JSFunction *
 JSScript::getFunction(size_t index)
 {
     JSObject *funobj = getObject(index);
 #ifdef DEBUG
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -341,23 +341,28 @@ WorkerThread::handleAsmJSWorkload(Worker
 
     asmData = state.asmJSWorklist.popCopy();
     bool success = false;
 
     state.unlock();
     do {
         ion::IonContext icx(asmData->mir->compartment, &asmData->mir->temp());
 
+        int64_t before = PRMJ_Now();
+
         if (!OptimizeMIR(asmData->mir))
             break;
 
         asmData->lir = GenerateLIR(asmData->mir);
         if (!asmData->lir)
             break;
 
+        int64_t after = PRMJ_Now();
+        asmData->compileTime = (after - before) / PRMJ_USEC_PER_MSEC;
+
         success = true;
     } while(0);
     state.lock();
 
     // On failure, signal parent for harvesting in CancelOutstandingJobs().
     if (!success) {
         asmData = NULL;
         state.noteAsmJSFailure(asmData->funcNum);
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-block-page-break-inside-avoid-8-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-block-page-break-inside-avoid-8-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-block-page-break-inside-avoid-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-block-page-break-inside-avoid-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 .test { page-break-after:always; }
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-2-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-2-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 .test { float:left; }
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-5-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-5-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
         html,body {
             color:black; background-color:white; font-size:16px; padding:0; margin:0;
         }
 p { height: 0.5in; width: 1in; margin:0; background-color:blue; float:left; }
 .test { float:left; clear:left; width:3.3in; }
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-6-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-6-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 html,body {
     color:black; background-color:white; font-size:16px; padding:0; margin:0; height:100%;
 }
 p { height:60%; width:90%; margin:0; background-color:blue; border:1px solid black; }
 .test { page-break-before:always; float:left; }
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-7-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-7-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged"><style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 html,body {
     color:black; background-color:white; font-size:16px; padding:0; margin:0; height:100%;
 }
 
 .test { 
   height:60%;
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-8-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-8-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged"><style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 html,body {
     color:black; background-color:white; font-size:16px; padding:0; margin:0; height:100%;
 }
 
 .test { 
   height:60%;
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-9-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-float-page-break-inside-avoid-9-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged"><style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 html,body {
     color:black; background-color:white; font-size:16px; padding:0; margin:0; height:100%;
 }
 
 .test { 
   height:60%;
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-inline-page-break-inside-avoid-1-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-inline-page-break-inside-avoid-1-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-4-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-4-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-5-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-5-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 thead { page-break-after:always; }
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-7-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-7-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 .bb { page-break-before:always; }
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-8-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-rowgroup-page-break-inside-avoid-8-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 .bb { page-break-before:always; }
 </style>
 </head>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-2-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-2-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-3-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-3-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 table { display:inline-table; }
 </style>
 </head>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-4-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-4-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 table { display:inline-table; }
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-5-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-5-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 tr,table { page-break-before:always; }
 </style>
 </head>
--- a/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-ref.html
+++ b/layout/reftests/w3c-css/submitted/css21/pagination/moz-css21-table-page-break-inside-avoid-ref.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en-US" class="reftest-print">
 <head>
   <title>CSS Test: CSS 2.1 page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
 <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 p { height: 1in; width: 1in; margin:0; background-color:blue; }
 
 </style>
 </head>
 <body>
--- a/layout/reftests/w3c-css/submitted/multicol3/moz-multicol3-column-balancing-break-inside-avoid-1-ref.html
+++ b/layout/reftests/w3c-css/submitted/multicol3/moz-multicol3-column-balancing-break-inside-avoid-1-ref.html
@@ -1,13 +1,12 @@
 <!DOCTYPE HTML>
 <html><head>
   <title>CSS Test: Balancing Overflow, page-break-inside:avoid</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=685012">
-  <link rel="help" href="http://www.w3.org/TR/CSS21/page.html#propdef-page-break-inside">
   <meta name="flags" content="paged">
   <meta charset="utf-8">
   <style type="text/css">
 @page { size:5in 3in; margin:0.5in; }
 
         html,body {
             color:black; background-color:white; font-size:16px; padding:0; margin:0;
         }
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/829817.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+@page {}
+
+</style>
+<script>
+
+function boom()
+{
+    // This shouldn't cause a shutdown leak.
+    document.styleSheets[0].cssRules[0].style.someExpando = "set an expando to preserve the wrapper";
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -83,13 +83,14 @@ load 729126-2.html
 skip-if(Android||browserIsRemote) load 786108-1.html # Bug 795534
 skip-if(browserIsRemote) load 786108-2.html # Bug 795534
 load 788836.html
 load 806310-1.html
 load 812824.html
 load 822842.html
 load 822766-1.html
 load 827591-1.html
+load 829817.html
 load 840898.html
 load 842134.html
 load 862113.html
 load 867487.html
 load 880862.html
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1204,16 +1204,19 @@ InitSystemMetrics()
   if (NS_SUCCEEDED(
         LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
                             &metricResult))) {
     nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
     switch(metricResult) {
       case LookAndFeel::eWindowsTheme_Aero:
         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
         break;
+      case LookAndFeel::eWindowsTheme_AeroLite:
+        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
+        break;
       case LookAndFeel::eWindowsTheme_LunaBlue:
         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
         break;
       case LookAndFeel::eWindowsTheme_LunaOlive:
         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
         break;
       case LookAndFeel::eWindowsTheme_LunaSilver:
         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -2256,17 +2256,17 @@ nsCSSKeyframeRule::Clone() const
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSKeyframeRule)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSKeyframeRule)
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSKeyframeRule)
   if (tmp->mDOMDeclaration) {
     tmp->mDOMDeclaration->DropReference();
-    ImplCycleCollectionUnlink(tmp->mDOMDeclaration);
+    tmp->mDOMDeclaration = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSKeyframeRule)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 DOMCI_DATA(MozCSSKeyframeRule, nsCSSKeyframeRule)
 
@@ -2739,23 +2739,33 @@ nsCSSPageRule::~nsCSSPageRule()
 
 /* virtual */ already_AddRefed<css::Rule>
 nsCSSPageRule::Clone() const
 {
   nsRefPtr<css::Rule> clone = new nsCSSPageRule(*this);
   return clone.forget();
 }
 
-NS_IMPL_ADDREF(nsCSSPageRule)
-NS_IMPL_RELEASE(nsCSSPageRule)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSPageRule)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSPageRule)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSPageRule)
+  if (tmp->mDOMDeclaration) {
+    tmp->mDOMDeclaration->DropReference();
+    tmp->mDOMDeclaration = nullptr;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSPageRule)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 DOMCI_DATA(CSSPageRule, nsCSSPageRule)
 
 // QueryInterface implementation for nsCSSPageRule
-NS_INTERFACE_MAP_BEGIN(nsCSSPageRule)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageRule)
   NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSPageRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSPageRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSPageRule, Rule)
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -549,17 +549,18 @@ public:
     : mDeclaration(aDeclaration),
       mImportantRule(nullptr)
   {
   }
 private:
   nsCSSPageRule(const nsCSSPageRule& aCopy);
   ~nsCSSPageRule();
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSPageRule, nsIDOMCSSPageRule)
 
   // nsIStyleRule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE;
 #endif
 
   // Rule methods
   DECL_STYLE_RULE_INHERIT
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -32,16 +32,17 @@ static const int32_t kScanKeywords[] = {
 struct WindowsThemeName {
     LookAndFeel::WindowsTheme id;
     const wchar_t* name;
 };
 
 // Windows theme identities used in the -moz-windows-theme media query.
 const WindowsThemeName themeStrings[] = {
     { LookAndFeel::eWindowsTheme_Aero,       L"aero" },
+    { LookAndFeel::eWindowsTheme_AeroLite,   L"aero-lite" },
     { LookAndFeel::eWindowsTheme_LunaBlue,   L"luna-blue" },
     { LookAndFeel::eWindowsTheme_LunaOlive,  L"luna-olive" },
     { LookAndFeel::eWindowsTheme_LunaSilver, L"luna-silver" },
     { LookAndFeel::eWindowsTheme_Royale,     L"royale" },
     { LookAndFeel::eWindowsTheme_Zune,       L"zune" },
     { LookAndFeel::eWindowsTheme_Generic,    L"generic" }
 };
 #endif
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -641,16 +641,17 @@ function run() {
   expression_should_not_be_parseable("-moz-windows-classic: true");
   expression_should_not_be_parseable("-moz-windows-glass: true");
   expression_should_not_be_parseable("-moz-touch-enabled: true");
   expression_should_not_be_parseable("-moz-maemo-classic: true");
   expression_should_not_be_parseable("-moz-swipe-animation-enabled: true");
 
   // windows theme media queries
   expression_should_be_parseable("-moz-windows-theme: aero");
+  expression_should_be_parseable("-moz-windows-theme: aero-lite");
   expression_should_be_parseable("-moz-windows-theme: luna-blue");
   expression_should_be_parseable("-moz-windows-theme: luna-olive");
   expression_should_be_parseable("-moz-windows-theme: luna-silver");
   expression_should_be_parseable("-moz-windows-theme: royale");
   expression_should_be_parseable("-moz-windows-theme: generic");
   expression_should_be_parseable("-moz-windows-theme: zune");
   expression_should_be_parseable("-moz-windows-theme: garbage");
   expression_should_not_be_parseable("-moz-windows-theme: ''");
--- a/mobile/android/base/DataReportingNotification.java
+++ b/mobile/android/base/DataReportingNotification.java
@@ -39,18 +39,19 @@ public class DataReportingNotification {
 
     public static void checkAndNotifyPolicy(Context context) {
         SharedPreferences dataPrefs = context.getSharedPreferences(DEFAULT_PREFS_BRANCH, 0);
 
         // Notify if user has not been notified or if policy version has changed.
         if ((!dataPrefs.contains(PREFS_POLICY_NOTIFIED_TIME)) ||
             (DATA_REPORTING_VERSION != dataPrefs.getInt(PREFS_POLICY_VERSION, -1))) {
 
-            // Launch Data Choices fragment when notification is clicked.
-            Intent prefIntent = new Intent(context, GeckoPreferences.class);
+            // Launch main App to launch Data choices when notification is clicked.
+            Intent prefIntent = new Intent(GeckoApp.ACTION_LAUNCH_SETTINGS);
+            prefIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS);
 
             // Build launch intent based on Android version.
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                 prefIntent.putExtra("resource", "preferences_datareporting");
             } else {
                 prefIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, GeckoPreferenceFragment.class.getName());
 
                 Bundle fragmentArgs = new Bundle();
--- a/mobile/android/base/DoorHangerPopup.java
+++ b/mobile/android/base/DoorHangerPopup.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko;
 
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 
 import android.graphics.drawable.BitmapDrawable;
+import android.os.Build;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.PopupWindow;
@@ -267,31 +268,40 @@ public class DoorHangerPopup extends Pop
             update();
             return;
         }
 
         int[] anchorLocation = new int[2];
         if (mAnchor != null)
             mAnchor.getLocationInWindow(anchorLocation);
 
+        // Make the popup focusable for accessibility. This gets done here
+        // so the node can be accessibility focused, but on pre-ICS devices this
+        // causes crashes, so it is done after the popup is shown.
+        if (Build.VERSION.SDK_INT >= 14) {
+            setFocusable(true);
+        }
+
         // If there's no anchor or the anchor is out of the window bounds,
         // just show the popup at the top of the gecko app view.
         if (mAnchor == null || anchorLocation[1] < 0) {
             showAtLocation(mActivity.getView(), Gravity.TOP, 0, 0);
-            return;
+        } else {
+            // On tablets, we need to position the popup so that the center of the arrow points to the
+            // center of the anchor view. On phones the popup stretches across the entire screen, so the
+            // arrow position is determined by its left margin.
+            int offset = HardwareUtils.isTablet() ? mAnchor.getWidth()/2 - mArrowWidth/2 -
+                         ((RelativeLayout.LayoutParams) mArrow.getLayoutParams()).leftMargin : 0;
+            showAsDropDown(mAnchor, offset, -mYOffset);
         }
 
-        // On tablets, we need to position the popup so that the center of the arrow points to the
-        // center of the anchor view. On phones the popup stretches across the entire screen, so the
-        // arrow position is determined by its left margin.
-        int offset = HardwareUtils.isTablet() ? mAnchor.getWidth()/2 - mArrowWidth/2 -
-                     ((RelativeLayout.LayoutParams) mArrow.getLayoutParams()).leftMargin : 0;
-        showAsDropDown(mAnchor, offset, -mYOffset);
-        // Make the popup focusable for keyboard accessibility.
-        setFocusable(true);
+        if (Build.VERSION.SDK_INT < 14) {
+            // Make the popup focusable for keyboard accessibility.
+            setFocusable(true);
+        }
     }
 
     private void showDividers() {
         int count = mContent.getChildCount();
 
         for (int i = 0; i < count; i++) {
             DoorHanger dh = (DoorHanger) mContent.getChildAt(i);
             dh.showDivider();
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -140,16 +140,17 @@ abstract public class GeckoApp
         PREFETCH    /* launched with a passed URL that we prefetch */
     }
 
     public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ACTION_ALERT_CALLBACK";
     public static final String ACTION_WEBAPP_PREFIX = "org.mozilla.gecko.WEBAPP";
     public static final String ACTION_DEBUG         = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_BOOKMARK      = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_LOAD          = "org.mozilla.gecko.LOAD";
+    public static final String ACTION_LAUNCH_SETTINGS = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_INIT_PW       = "org.mozilla.gecko.INIT_PW";
     public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
     public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
 
     public static final String PREFS_NAME          = "GeckoApp";
     public static final String PREFS_OOM_EXCEPTION = "OOMException";
     public static final String PREFS_WAS_STOPPED   = "wasStopped";
     public static final String PREFS_CRASHED       = "crashed";
@@ -1429,16 +1430,24 @@ abstract public class GeckoApp
                 @Override
                 public void run() {
                     GeckoThread.setLaunchState(GeckoThread.LaunchState.Launching);
                     sGeckoThread.start();
                 }
             }, 1000 * 5 /* 5 seconds */);
         }
 
+        // Check if launched from data reporting notification.
+        if (ACTION_LAUNCH_SETTINGS.equals(action)) {
+            Intent settingsIntent = new Intent(GeckoApp.sAppContext, GeckoPreferences.class);
+            // Copy extras.
+            settingsIntent.putExtras(intent);
+            startActivity(settingsIntent);
+        }
+
         //app state callbacks
         mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>();
 
         //register for events
         registerEventListener("log");
         registerEventListener("Reader:ListCountRequest");
         registerEventListener("Reader:Added");
         registerEventListener("Reader:Removed");
@@ -1778,16 +1787,22 @@ abstract public class GeckoApp
         } else if (ACTION_BOOKMARK.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri));
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createURILoadEvent(uri));
         } else if (ACTION_ALERT_CALLBACK.equals(action)) {
             processAlertCallback(intent);
+        } if (ACTION_LAUNCH_SETTINGS.equals(action)) {
+            // Check if launched from data reporting notification.
+            Intent settingsIntent = new Intent(GeckoApp.sAppContext, GeckoPreferences.class);
+            // Copy extras.
+            settingsIntent.putExtras(intent);
+            startActivity(settingsIntent);
         }
     }
 
     /*
      * Handles getting a uri from and intent in a way that is backwards
      * compatable with our previous implementations
      */
     protected String getURIFromIntent(Intent intent) {
--- a/mobile/android/chrome/content/SelectHelper.js
+++ b/mobile/android/chrome/content/SelectHelper.js
@@ -18,114 +18,109 @@ var SelectHelper = {
 
     this._uiBusy = true;
     this.show(aTarget);
     this._uiBusy = false;
   },
 
   show: function(aElement) {
     let list = this.getListForElement(aElement);
-    let data = JSON.parse(sendMessageToJava(list));
-    let selected = data.button;
-    if (selected == -1)
-        return;
+
+    let p = new Prompt({
+      window: aElement.contentDocument
+    });
 
-    var changed = false;
-    if (aElement instanceof Ci.nsIDOMXULMenuListElement) {
-      aElement.selectedIndex = selected;
-    } else if (aElement instanceof HTMLSelectElement) {
-      if (!(selected instanceof Array)) {
-        let temp = [];
-        for (let i = 0; i < list.listitems.length; i++) {
-          temp[i] = (i == selected);
-        }
-        selected = temp;
-      }
-      let i = 0;
-      this.forOptions(aElement, function(aNode) {
-        if (aNode.selected != selected[i])
-          changed = true;
-        aNode.selected = selected[i++];
-      });
+    if (aElement.multiple) {
+      p.addButton({
+        label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog")
+      }).setMultiChoiceItems(list);
+    } else {
+      p.setSingleChoiceItems(list);
     }
 
-    if (changed)
-      this.fireOnChange(aElement);
+    p.show((function(data) {
+      let selected = data.button;
+      if (selected == -1)
+          return;
+
+      let changed = false;
+      if (aElement instanceof Ci.nsIDOMXULMenuListElement) {
+        aElement.selectedIndex = selected;
+      } else if (aElement instanceof HTMLSelectElement) {
+        if (!Array.isArray(selected)) {
+          let temp = [];
+          for (let i = 0; i <= list.length; i++) {
+            temp[i] = (i == selected);
+          }
+          selected = temp;
+        }
+
+        let i = 0;
+        this.forOptions(aElement, function(aNode) {
+          if (aNode.selected != selected[i]) {
+            changed = true;
+            aNode.selected = selected[i];
+          }
+          i++
+        });
+      }
+
+      if (changed)
+        this.fireOnChange(aElement);
+    }).bind(this));
   },
 
   _isMenu: function(aElement) {
     return (aElement instanceof HTMLSelectElement ||
             aElement instanceof Ci.nsIDOMXULMenuListElement);
   },
 
   getListForElement: function(aElement) {
-    let result = {
-      type: "Prompt:Show",
-      multiple: aElement.multiple,
-      selected: [],
-      listitems: []
-    };
-
-    if (aElement.multiple) {
-      result.buttons = [
-        Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog")
-      ];
-    }
-
     let index = 0;
-    this.forOptions(aElement, function(aNode, aOptions) {
+    let items = [];
+    this.forOptions(aElement, function(aNode, aOptions, aParent) {
       let item = {
         label: aNode.text || aNode.label,
-        isGroup: aOptions.isGroup,
-        inGroup: aOptions.inGroup,
+        header: aOptions.isGroup,
         disabled: aNode.disabled,
-        id: index
+        id: index,
+        selected: aNode.selected
       }
-      if (aOptions.inGroup)
-        item.disabled = item.disabled || aNode.parentNode.disabled;
 
-      result.listitems[index] = item;
-      result.selected[index] = aNode.selected;
+      if (aParent) {
+        item.child = true;
+        item.disabled = item.disabled || aParent.disabled;
+      }
+      items.push(item);
+
       index++;
     });
-    return result;
+    return items;
   },
 
-  forOptions: function(aElement, aFunction) {
-    let parent = aElement;
+  forOptions: function(aElement, aFunction, aParent = null) {
+    let element = aElement;
     if (aElement instanceof Ci.nsIDOMXULMenuListElement)
-      parent = aElement.menupopup;
-    let children = parent.children;
+      element = aElement.menupopup;
+    let children = element.children;
     let numChildren = children.length;
 
     // if there are no children in this select, we add a dummy row so that at least something appears
     if (numChildren == 0)
-      aFunction.call(this, { label: "" }, { isGroup: false, inGroup: false });
+      aFunction.call(this, { label: "" }, { isGroup: false }, aParent);
 
     for (let i = 0; i < numChildren; i++) {
       let child = children[i];
       if (child instanceof HTMLOptionElement ||
           child instanceof Ci.nsIDOMXULSelectControlItemElement) {
-        // This is a regular choice under no group.
-        aFunction.call(this, child, {
-          isGroup: false, inGroup: false
-        });
+        aFunction.call(this, child, { isGroup: false }, aParent);
       } else if (child instanceof HTMLOptGroupElement) {
-        aFunction.call(this, child, {
-          isGroup: true, inGroup: false
-        });
+        aFunction.call(this, child, { isGroup: true });
+        this.forOptions(child, aFunction, child);
 
-        let subchildren = child.children;
-        let numSubchildren = subchildren.length;
-        for (let j = 0; j < numSubchildren; j++) {
-          let subchild = subchildren[j];
-          aFunction.call(this, subchild, {
-            isGroup: false, inGroup: true
-          });
-        }
       }
     }
   },
 
   fireOnChange: function(aElement) {
     let evt = aElement.ownerDocument.createEvent("Events");
     evt.initEvent("change", true, true, aElement.defaultView, 0,
                   false, false,
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -90,16 +90,18 @@ XPIDL_SOURCES += [
     'nsIStandardURL.idl',
     'nsIStreamListener.idl',
     'nsIStreamListenerTee.idl',
     'nsIStreamLoader.idl',
     'nsIStreamTransportService.idl',
     'nsIStrictTransportSecurityService.idl',
     'nsISyncStreamListener.idl',
     'nsISystemProxySettings.idl',
+    'nsIThreadRetargetableRequest.idl',
+    'nsIThreadRetargetableStreamListener.idl',
     'nsITimedChannel.idl',
     'nsITraceableChannel.idl',
     'nsITransport.idl',
     'nsIUDPServerSocket.idl',
     'nsIURI.idl',
     'nsIURIChecker.idl',
     'nsIURIClassifier.idl',
     'nsIURIWithPrincipal.idl',
--- a/netwerk/base/public/nsIInputStreamPump.idl
+++ b/netwerk/base/public/nsIInputStreamPump.idl
@@ -6,20 +6,21 @@
 
 interface nsIInputStream;
 interface nsIStreamListener;
 
 /**
  * nsIInputStreamPump
  *
  * This interface provides a means to configure and use a input stream pump
- * instance.  The input stream pump will asynchronously read from a input
- * stream, and push data to a nsIStreamListener instance.  It utilizes the
+ * instance.  The input stream pump will asynchronously read from an input
+ * stream, and push data to an nsIStreamListener instance.  It utilizes the
  * current thread's nsIEventTarget in order to make reading from the stream
- * asynchronous.
+ * asynchronous. A different thread can be used if the pump also implements
+ * nsIThreadRetargetableRequest.
  *
  * If the given stream supports nsIAsyncInputStream, then the stream pump will
  * call the stream's AsyncWait method to drive the stream listener.  Otherwise,
  * the stream will be read on a background thread utilizing the stream
  * transport service.  More details are provided below.
  */
 [scriptable, uuid(400F5468-97E7-4d2b-9C65-A82AECC7AE82)]
 interface nsIInputStreamPump : nsIRequest
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIThreadRetargetableRequest.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIEventTarget.idl"
+
+/**
+ * nsIThreadRetargetableRequest
+ *
+ * Should be implemented by requests that support retargeting delivery of
+ * OnDataAvailable and OnStopRequest off the main thread.
+ */
+[uuid(27b84c48-5a73-4ba4-a8a4-8b5e649a145e)]
+interface nsIThreadRetargetableRequest : nsISupports
+{
+  /**
+   * Called to retarget delivery of OnDataAvailable and OnStopRequest to
+   * another thread. Should only be called within the context of OnStartRequest
+   * on the main thread.
+   *
+   * @param aNewTarget New event target, e.g. thread or threadpool.
+   *
+   * Note: no return value is given. If the retargeting cannot be handled,
+   * normal delivery to the main thread will continue. As such, listeners
+   * should be ready to deal with OnDataAvailable and OnStopRequest on
+   * either the main thread or the new target thread.
+   */
+  void retargetDeliveryTo(in nsIEventTarget aNewTarget);
+};
+
+
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIThreadRetargetableStreamListener.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * nsIThreadRetargetableListener
+ *
+ * To be used by classes which implement nsIStreamListener and whose
+ * OnDataAvailable and OnStopRequest may be retargeted for delivery off the
+ * main thread.
+ */
+[uuid(fb2304b8-f82f-4433-af68-d874a2ebbdc1)]
+interface nsIThreadRetargetableStreamListener : nsISupports
+{
+  /**
+   * Checks this listener and any next listeners it may have to verify that
+   * they can receive OnDataAvailable and OnStopRequest off the main thread.
+   * It is the responsibility of the implementing class to decide on the
+   * criteria to determine if retargeted delivery of these methods is
+   * possible, but it must check any and all nsIStreamListener objects that
+   * might be called in the listener chain.
+   *
+   * An exception should be thrown if a listener in the chain does not
+   * support retargeted delivery, i.e. if the next listener does not implement
+   * nsIThreadRetargetableStreamListener, or a call to its checkListenerChain()
+   * fails.
+   */
+  void checkListenerChain();
+};
+
--- a/netwerk/base/public/nsStreamListenerWrapper.h
+++ b/netwerk/base/public/nsStreamListenerWrapper.h
@@ -3,32 +3,35 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsStreamListenerWrapper_h__
 #define nsStreamListenerWrapper_h__
 
 #include "nsCOMPtr.h"
 #include "nsIStreamListener.h"
 #include "nsIRequestObserver.h"
+#include "nsIThreadRetargetableStreamListener.h"
 #include "mozilla/Attributes.h"
 
 // Wrapper class to make replacement of nsHttpChannel's listener
 // from JavaScript possible. It is workaround for bug 433711 and 682305.
 class nsStreamListenerWrapper MOZ_FINAL : public nsIStreamListener
+                                        , public nsIThreadRetargetableStreamListener
 {
 public:
   nsStreamListenerWrapper(nsIStreamListener *listener)
     : mListener(listener)
   {
     NS_ASSERTION(mListener, "no stream listener specified");
   }
 
   NS_DECL_ISUPPORTS
   NS_FORWARD_NSIREQUESTOBSERVER(mListener->)
   NS_FORWARD_NSISTREAMLISTENER(mListener->)
+  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
 private:
   ~nsStreamListenerWrapper() {}
   nsCOMPtr<nsIStreamListener> mListener;
 };
 
 #endif // nsStreamListenerWrapper_h__
 
--- a/netwerk/base/src/nsInputStreamPump.cpp
+++ b/netwerk/base/src/nsInputStreamPump.cpp
@@ -6,20 +6,23 @@
 
 #include "nsIOService.h"
 #include "nsInputStreamPump.h"
 #include "nsIServiceManager.h"
 #include "nsIStreamTransportService.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsISeekableStream.h"
 #include "nsITransport.h"
+#include "nsIThreadRetargetableStreamListener.h"
+#include "nsStreamUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
 #include "prlog.h"
+#include "nsPrintfCString.h"
 #include "GeckoProfiler.h"
 #include <algorithm>
 
 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
 
 #if defined(PR_LOGGING)
 //
 // NSPR_LOG_MODULES=nsStreamPump:5
@@ -36,16 +39,17 @@ nsInputStreamPump::nsInputStreamPump()
     : mState(STATE_IDLE)
     , mStreamOffset(0)
     , mStreamLength(UINT64_MAX)
     , mStatus(NS_OK)
     , mSuspendCount(0)
     , mLoadFlags(LOAD_NORMAL)
     , mWaiting(false)
     , mCloseWhenDone(false)
+    , mRetargeting(false)
 {
 #if defined(PR_LOGGING)
     if (!gStreamPumpLog)
         gStreamPumpLog = PR_NewLogModule("nsStreamPump");
 #endif
 }
 
 nsInputStreamPump::~nsInputStreamPump()
@@ -114,38 +118,43 @@ nsInputStreamPump::PeekStream(PeekSegmen
                                     nsIOService::gDefaultSegmentSize,
                                     &dummy);
 }
 
 nsresult
 nsInputStreamPump::EnsureWaiting()
 {
     // no need to worry about multiple threads... an input stream pump lives
-    // on only one thread.
-
+    // on only one thread at a time.
+    MOZ_ASSERT(mAsyncStream);
     if (!mWaiting) {
+        MOZ_ASSERT(mTargetThread);
         nsresult rv = mAsyncStream->AsyncWait(this, 0, 0, mTargetThread);
         if (NS_FAILED(rv)) {
             NS_ERROR("AsyncWait failed");
             return rv;
         }
+        // Any retargeting during STATE_START or START_TRANSFER is complete
+        // after the call to AsyncWait; next callback wil be on mTargetThread.
+        mRetargeting = false;
         mWaiting = true;
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsInputStreamPump::nsISupports
 //-----------------------------------------------------------------------------
 
 // although this class can only be accessed from one thread at a time, we do
 // allow its ownership to move from thread to thread, assuming the consumer
 // understands the limitations of this.
-NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamPump,
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsInputStreamPump,
                               nsIRequest,
+                              nsIThreadRetargetableRequest,
                               nsIInputStreamCallback,
                               nsIInputStreamPump)
 
 //-----------------------------------------------------------------------------
 // nsInputStreamPump::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -375,25 +384,40 @@ nsInputStreamPump::OnInputStreamReady(ns
             nextState = OnStateStop();
             break;
         default:
             nextState = 0;
             NS_NOTREACHED("Unknown enum value.");
             return NS_ERROR_UNEXPECTED;
         }
 
-        if (mState == nextState && !mSuspendCount) {
-            NS_ASSERTION(mState == STATE_TRANSFER, "unexpected state");
-            NS_ASSERTION(NS_SUCCEEDED(mStatus), "unexpected status");
+        bool stillTransferring = (mState == STATE_TRANSFER &&
+                                  nextState == STATE_TRANSFER);
+        if (stillTransferring) {
+            NS_ASSERTION(NS_SUCCEEDED(mStatus),
+                         "Should not have failed status for ongoing transfer");
+        } else {
+            NS_ASSERTION(mState != nextState,
+                         "Only OnStateTransfer can be called more than once.");
+        }
+        if (mRetargeting) {
+            NS_ASSERTION(mState != STATE_STOP,
+                         "Retargeting should not happen during OnStateStop.");
+        }
 
+        // Wait asynchronously if there is still data to transfer, or if
+        // delivery of data has been requested on another thread.
+        if (!mSuspendCount && (stillTransferring || mRetargeting)) {
+            mState = nextState;
             mWaiting = false;
             mStatus = EnsureWaiting();
             if (NS_SUCCEEDED(mStatus))
                 break;
             
+            // Failure to start asynchronous wait: stop transfer.
             nextState = STATE_STOP;
         }
 
         mState = nextState;
     }
     return NS_OK;
 }
 
@@ -546,18 +570,53 @@ nsInputStreamPump::OnStateStop()
     if (NS_FAILED(mStatus))
         mAsyncStream->CloseWithStatus(mStatus);
     else if (mCloseWhenDone)
         mAsyncStream->Close();
 
     mAsyncStream = 0;
     mTargetThread = 0;
     mIsPending = false;
+    mRetargeting = false;
 
     mListener->OnStopRequest(this, mListenerContext, mStatus);
     mListener = 0;
     mListenerContext = 0;
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
 
     return STATE_IDLE;
 }
+
+//-----------------------------------------------------------------------------
+// nsIThreadRetargetableRequest
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsInputStreamPump::RetargetDeliveryTo(nsIEventTarget* aNewTarget)
+{
+    NS_ENSURE_ARG(aNewTarget);
+    if (aNewTarget == mTargetThread) {
+        NS_WARNING("Retargeting delivery to same thread");
+        return NS_OK;
+    }
+    NS_ENSURE_TRUE(mState == STATE_START || mState == STATE_TRANSFER,
+                   NS_ERROR_UNEXPECTED);
+
+    // Ensure that |mListener| and any subsequent listeners can be retargeted
+    // to another thread.
+    nsresult rv = NS_OK;
+    nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+        do_QueryInterface(mListener, &rv);
+    if (NS_SUCCEEDED(rv) && retargetableListener) {
+        rv = retargetableListener->CheckListenerChain();
+        if (NS_SUCCEEDED(rv)) {
+            mTargetThread = aNewTarget;
+            mRetargeting = true;
+        }
+    }
+    LOG(("nsInputStreamPump::RetargetDeliveryTo [this=%x aNewTarget=%p] "
+         "%s listener [%p] rv[%x]",
+         this, aNewTarget, (mTargetThread == aNewTarget ? "success" : "failure"),
+         (nsIStreamListener*)mListener, rv));
+    return rv;
+}
--- a/netwerk/base/src/nsInputStreamPump.h
+++ b/netwerk/base/src/nsInputStreamPump.h
@@ -10,27 +10,30 @@
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIThread.h"
+#include "nsIThreadRetargetableRequest.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Attributes.h"
 
 class nsInputStreamPump MOZ_FINAL : public nsIInputStreamPump
                                   , public nsIInputStreamCallback
+                                  , public nsIThreadRetargetableRequest
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUEST
     NS_DECL_NSIINPUTSTREAMPUMP
     NS_DECL_NSIINPUTSTREAMCALLBACK
+    NS_DECL_NSITHREADRETARGETABLEREQUEST
 
     nsInputStreamPump(); 
     ~nsInputStreamPump();
 
     static NS_HIDDEN_(nsresult)
                       Create(nsInputStreamPump  **result,
                              nsIInputStream      *stream,
                              int64_t              streamPos = -1,
@@ -66,24 +69,25 @@ protected:
     uint32_t OnStateStart();
     uint32_t OnStateTransfer();
     uint32_t OnStateStop();
 
     uint32_t                      mState;
     nsCOMPtr<nsILoadGroup>        mLoadGroup;
     nsCOMPtr<nsIStreamListener>   mListener;
     nsCOMPtr<nsISupports>         mListenerContext;
-    nsCOMPtr<nsIThread>           mTargetThread;
+    nsCOMPtr<nsIEventTarget>      mTargetThread;
     nsCOMPtr<nsIInputStream>      mStream;
     nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
     uint64_t                      mStreamOffset;
     uint64_t                      mStreamLength;
     uint32_t                      mSegSize;
     uint32_t                      mSegCount;
     nsresult                      mStatus;
     uint32_t                      mSuspendCount;
     uint32_t                      mLoadFlags;
     bool                          mIsPending;
     bool                          mWaiting; // true if waiting on async source
     bool                          mCloseWhenDone;
+    bool                          mRetargeting;
 };
 
 #endif // !nsInputStreamChannel_h__
--- a/netwerk/base/src/nsStreamListenerTee.cpp
+++ b/netwerk/base/src/nsStreamListenerTee.cpp
@@ -1,19 +1,20 @@
 /* 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 "nsStreamListenerTee.h"
 #include "nsProxyRelease.h"
 
-NS_IMPL_ISUPPORTS3(nsStreamListenerTee,
-                   nsIStreamListener,
-                   nsIRequestObserver,
-                   nsIStreamListenerTee)
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsStreamListenerTee,
+                              nsIStreamListener,
+                              nsIRequestObserver,
+                              nsIStreamListenerTee,
+                              nsIThreadRetargetableStreamListener)
 
 NS_IMETHODIMP
 nsStreamListenerTee::OnStartRequest(nsIRequest *request,
                                     nsISupports *context)
 {
     NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED);
     nsresult rv1 = mListener->OnStartRequest(request, context);
     nsresult rv2 = NS_OK;
@@ -88,16 +89,39 @@ nsStreamListenerTee::OnDataAvailable(nsI
         tee = do_QueryInterface(mInputTee, &rv);
         if (NS_FAILED(rv)) return rv;
     }
 
     return mListener->OnDataAvailable(request, context, tee, offset, count);
 }
 
 NS_IMETHODIMP
+nsStreamListenerTee::CheckListenerChain()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
+    nsresult rv = NS_OK;
+    nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+        do_QueryInterface(mListener, &rv);
+    if (retargetableListener) {
+        rv = retargetableListener->CheckListenerChain();
+    }
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    if (!mObserver) {
+      return rv;
+    }
+    retargetableListener = do_QueryInterface(mObserver, &rv);
+    if (retargetableListener) {
+        rv = retargetableListener->CheckListenerChain();
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
 nsStreamListenerTee::Init(nsIStreamListener *listener,
                           nsIOutputStream *sink,
                           nsIRequestObserver *requestObserver)
 {
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_ARG_POINTER(sink);
     mListener = listener;
     mSink = sink;
--- a/netwerk/base/src/nsStreamListenerTee.h
+++ b/netwerk/base/src/nsStreamListenerTee.h
@@ -1,27 +1,30 @@
 /* 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/. */
 
 #ifndef nsStreamListenerTee_h__
 #define nsStreamListenerTee_h__
 
 #include "nsIStreamListenerTee.h"
+#include "nsIThreadRetargetableStreamListener.h"
 #include "nsIInputStreamTee.h"
 #include "nsIOutputStream.h"
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 
 class nsStreamListenerTee : public nsIStreamListenerTee
+                          , public nsIThreadRetargetableStreamListener
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     NS_DECL_NSISTREAMLISTENERTEE
 
     nsStreamListenerTee() { }
     virtual ~nsStreamListenerTee() { }
 
 private:
     nsCOMPtr<nsIInputStreamTee>  mInputTee;
     nsCOMPtr<nsIOutputStream>    mSink;
--- a/netwerk/base/src/nsStreamListenerWrapper.cpp
+++ b/netwerk/base/src/nsStreamListenerWrapper.cpp
@@ -1,10 +1,24 @@
 /* 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 "nsStreamListenerWrapper.h"
+#include "nsThreadUtils.h"
 
-NS_IMPL_ISUPPORTS2(nsStreamListenerWrapper,
+NS_IMPL_ISUPPORTS3(nsStreamListenerWrapper,
                    nsIStreamListener,
-                   nsIRequestObserver)
+                   nsIRequestObserver,
+                   nsIThreadRetargetableStreamListener)
 
+NS_IMETHODIMP
+nsStreamListenerWrapper::CheckListenerChain()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
+    nsresult rv = NS_OK;
+    nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+        do_QueryInterface(mListener, &rv);
+    if (retargetableListener) {
+        rv = retargetableListener->CheckListenerChain();
+    }
+    return rv;
+}
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -24,16 +24,17 @@
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsICacheService.h"
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
 #include "nsIRedirectResultListener.h"
 #include "mozilla/TimeStamp.h"
 #include "nsError.h"
+#include "nsPrintfCString.h"
 #include "nsAlgorithm.h"
 #include "GeckoProfiler.h"
 #include "nsIConsoleService.h"
 #include "base/compiler_specific.h"
 #include "NullHttpTransaction.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/VisualEventTracer.h"
 
@@ -4247,16 +4248,18 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
     NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
+    NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
+    NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::Cancel(nsresult status)
@@ -4938,16 +4941,52 @@ nsresult
 nsHttpChannel::ContinueOnStartRequest3(nsresult result)
 {
     if (mFallingBack)
         return NS_OK;
 
     return CallOnStartRequest();
 }
 
+class OnStopRequestCleanupEvent : public nsRunnable
+{
+public:
+    OnStopRequestCleanupEvent(nsHttpChannel *aHttpChannel,
+                              nsresult aStatus)
+    : mHttpChannel(aHttpChannel)
+    , mStatus(aStatus)
+    {
+        MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
+        NS_ASSERTION(aHttpChannel, "aHttpChannel should not be null");
+    }
+
+    NS_IMETHOD Run()
+    {
+        MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
+        if (mHttpChannel) {
+            mHttpChannel->OnStopRequestCleanup(mStatus);
+        }
+        return NS_OK;
+    }
+private:
+    nsRefPtr<nsHttpChannel> mHttpChannel;
+    nsresult mStatus;
+};
+
+nsresult
+nsHttpChannel::OnStopRequestCleanup(nsresult aStatus)
+{
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread");
+    if (mLoadGroup) {
+        mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+    }
+    ReleaseListeners();
+    return NS_OK;
+}
+
 NS_IMETHODIMP
 nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
 {
     PROFILER_LABEL("network", "nsHttpChannel::OnStopRequest");
     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
         this, request, status));
 
     if (mTimingEnabled && request == mCachePump) {
@@ -5067,31 +5106,65 @@ nsHttpChannel::OnStopRequest(nsIRequest 
 
     MOZ_EVENT_TRACER_DONE(this, "net::http::channel");
 
     CloseCacheEntry(!contentComplete);
 
     if (mOfflineCacheEntry)
         CloseOfflineCacheEntry();
 
-    if (mLoadGroup)
-        mLoadGroup->RemoveRequest(this, nullptr, status);
+    if (NS_IsMainThread()) {
+        OnStopRequestCleanup(status);
+    } else {
+        nsresult rv = NS_DispatchToMainThread(
+            new OnStopRequestCleanupEvent(this, status));
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
 
     // We don't need this info anymore
     CleanRedirectCacheChainIfNecessary();
 
-    ReleaseListeners();
-
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIStreamListener
 //-----------------------------------------------------------------------------
 
+class OnTransportStatusAsyncEvent : public nsRunnable
+{
+public:
+    OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink,
+                                nsresult aTransportStatus,
+                                uint64_t aProgress,
+                                uint64_t aProgressMax)
+    : mEventSink(aEventSink)
+    , mTransportStatus(aTransportStatus)
+    , mProgress(aProgress)
+    , mProgressMax(aProgressMax)
+    {
+        MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
+    }
+
+    NS_IMETHOD Run()
+    {
+        MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
+        if (mEventSink) {
+            mEventSink->OnTransportStatus(nullptr, mTransportStatus,
+                                          mProgress, mProgressMax);
+        }
+        return NS_OK;
+    }
+private:
+    nsCOMPtr<nsITransportEventSink> mEventSink;
+    nsresult mTransportStatus;
+    uint64_t mProgress;
+    uint64_t mProgressMax;
+};
+
 NS_IMETHODIMP
 nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
                                nsIInputStream *input,
                                uint64_t offset, uint32_t count)
 {
     PROFILER_LABEL("network", "nsHttpChannel::OnDataAvailable");
     LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%llu count=%u]\n",
         this, request, offset, count));
@@ -5126,17 +5199,24 @@ nsHttpChannel::OnDataAvailable(nsIReques
         // holds our best estimate of the total content length.  Even in the case
         // of a byte range request, the content length stored in the cached
         // response headers is what we want to use here.
 
         uint64_t progressMax(uint64_t(mResponseHead->ContentLength()));
         uint64_t progress = mLogicalOffset + uint64_t(count);
         MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
 
-        OnTransportStatus(nullptr, transportStatus, progress, progressMax);
+        if (NS_IsMainThread()) {
+            OnTransportStatus(nullptr, transportStatus, progress, progressMax);
+        } else {
+            nsresult rv = NS_DispatchToMainThread(
+                new OnTransportStatusAsyncEvent(this, transportStatus,
+                                                progress, progressMax));
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
 
         //
         // we have to manually keep the logical offset of the stream up-to-date.
         // we cannot depend solely on the offset provided, since we may have
         // already streamed some data from another source (see, for example,
         // OnDoneReadingPartialCacheEntry).
         //
         if (!mLogicalOffset)
@@ -5151,16 +5231,81 @@ nsHttpChannel::OnDataAvailable(nsIReques
             mLogicalOffset = progress;
         return rv;
     }
 
     return NS_ERROR_ABORT;
 }
 
 //-----------------------------------------------------------------------------
+// nsHttpChannel::nsIThreadRetargetableRequest
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget)
+{
+    MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
+
+    NS_ENSURE_ARG(aNewTarget);
+    if (aNewTarget == NS_GetCurrentThread()) {
+        NS_WARNING("Retargeting delivery to same thread");
+        return NS_OK;
+    }
+    NS_ENSURE_TRUE(mTransactionPump || mCachePump, NS_ERROR_NOT_AVAILABLE);
+
+    nsresult rv = NS_OK;
+    // If both cache pump and transaction pump exist, we're probably dealing
+    // with partially cached content. So, we must be able to retarget both.
+    nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
+    nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
+    if (mCachePump) {
+        // Direct call to QueryInterface to avoid multiple inheritance issues.
+        rv = mCachePump->QueryInterface(NS_GET_IID(nsIThreadRetargetableRequest),
+                                        getter_AddRefs(retargetableCachePump));
+        // nsInputStreamPump should implement this interface.
+        MOZ_ASSERT(retargetableCachePump);
+        rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
+    }
+    if (NS_SUCCEEDED(rv) && mTransactionPump) {
+        // Direct call to QueryInterface to avoid multiple inheritance issues.
+        rv = mTransactionPump->QueryInterface(NS_GET_IID(nsIThreadRetargetableRequest),
+                                              getter_AddRefs(retargetableTransactionPump));
+        // nsInputStreamPump should implement this interface.
+        MOZ_ASSERT(retargetableTransactionPump);
+        rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
+
+        // If retarget fails for transaction pump, we must restore mCachePump.
+        if (NS_FAILED(rv) && retargetableCachePump) {
+            nsCOMPtr<nsIThread> mainThread;
+            rv = NS_GetMainThread(getter_AddRefs(mainThread));
+            NS_ENSURE_SUCCESS(rv, rv);
+            rv = retargetableCachePump->RetargetDeliveryTo(mainThread);
+        }
+    }
+    return rv;
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpChannel::nsThreadRetargetableStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpChannel::CheckListenerChain()
+{
+    NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
+    nsresult rv = NS_OK;
+    nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+        do_QueryInterface(mListener, &rv);
+    if (retargetableListener) {
+        rv = retargetableListener->CheckListenerChain();
+    }
+    return rv;
+}
+
+//-----------------------------------------------------------------------------
 // nsHttpChannel::nsITransportEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
                                  uint64_t progress, uint64_t progressMax)
 {
     // cache the progress sink so we don't have to query for it each time.
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -23,16 +23,18 @@
 #include "nsIResumableChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIHttpChannelAuthProvider.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsITimedChannel.h"
 #include "nsIFile.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "nsIThreadRetargetableStreamListener.h"
 #include "nsDNSPrefetch.h"
 #include "TimingStruct.h"
 #include "AutoClose.h"
 #include "mozilla/Telemetry.h"
 
 class nsAHttpConnection;
 
 namespace mozilla { namespace net {
@@ -49,31 +51,35 @@ class nsHttpChannel : public HttpBaseCha
                     , public nsICachingChannel
                     , public nsICacheListener
                     , public nsITransportEventSink
                     , public nsIProtocolProxyCallback
                     , public nsIHttpAuthenticableChannel
                     , public nsIApplicationCacheChannel
                     , public nsIAsyncVerifyRedirectCallback
                     , public nsITimedChannel
+                    , public nsIThreadRetargetableRequest
+                    , public nsIThreadRetargetableStreamListener
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     NS_DECL_NSICACHEINFOCHANNEL
     NS_DECL_NSICACHINGCHANNEL
     NS_DECL_NSICACHELISTENER
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIPROTOCOLPROXYCALLBACK
     NS_DECL_NSIPROXIEDCHANNEL
     NS_DECL_NSIAPPLICATIONCACHECONTAINER
     NS_DECL_NSIAPPLICATIONCACHECHANNEL
     NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
     NS_DECL_NSITIMEDCHANNEL
+    NS_DECL_NSITHREADRETARGETABLEREQUEST
 
     // nsIHttpAuthenticableChannel. We can't use
     // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
     // others.
     NS_IMETHOD GetIsSSL(bool *aIsSSL);
     NS_IMETHOD GetProxyMethodIsConnect(bool *aProxyMethodIsConnect);
     NS_IMETHOD GetServerResponseHeader(nsACString & aServerResponseHeader);
     NS_IMETHOD GetProxyChallenges(nsACString & aChallenges);
@@ -145,16 +151,18 @@ public: /* internal necko use only */
              , mCacheKey(key)
         {}
 
         nsresult MarkAsForeign();
     };
 
     OfflineCacheEntryAsForeignMarker* GetOfflineCacheEntryAsForeignMarker();
 
+    nsresult OnStopRequestCleanup(nsresult aStatus);
+
 private:
     typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
 
     bool     RequestIsConditional();
     nsresult BeginConnect();
     nsresult Connect();
     nsresult ContinueConnect();
     void     SpeculativeConnect();
--- a/netwerk/test/httpserver/httpd.js
+++ b/netwerk/test/httpserver/httpd.js
@@ -3969,17 +3969,20 @@ Response.prototype =
     if (!this._processAsync)
     {
       dumpn("*** non-async response, set Content-Length");
 
       var bodyStream = this._bodyInputStream;
       var avail = bodyStream ? bodyStream.available() : 0;
 
       // XXX assumes stream will always report the full amount of data available
-      headers.setHeader("Content-Length", "" + avail, false);
+      // Set "Content-Length" if not already set by request handler.
+      if (!headers.hasHeader("Content-Length")) {
+        headers.setHeader("Content-Length", "" + avail, false);
+      }
     }
 
 
     // construct and send response
     dumpn("*** header post-processing completed, sending response head...");
 
     // request-line
     var preambleData = [statusLine];
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/Makefile.in
@@ -0,0 +1,21 @@
+# -*- Mode: Makefile; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# vim: set ts=8 sts=2 et sw=2 tw=80:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH		= @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir	= @relativesrcdir@
+FAIL_ON_WARNINGS := 1
+
+include $(DEPTH)/config/autoconf.mk
+
+MOCHITEST_FILES = \
+  partial_content.sjs \
+  test_partially_cached_content.html \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MODULE = 'test_necko'
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/partial_content.sjs
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// HTTP Version from request, used for response.
+var gHttpVersion;
+
+/* Debug and Error wrapper functions for dump().
+ */
+function ERR(response, responseCode, responseCodeStr, msg)
+{
+  // Dump to console log and send to client in response.
+  dump("SERVER ERROR: " + msg + "\n");
+  response.setStatusLine(gHttpVersion, responseCode, responseCodeStr);
+  response.write(msg);
+}
+
+function DBG(msg)
+{
+  // Dump to console only.
+  dump("SERVER DEBUG: " + msg + "\n");
+}
+
+/* Delivers content in parts to test partially cached content: requires two
+ * requests for the same resource.
+ *
+ * First call will respond with partial content, but a 200 header and
+ * Content-Length equal to the full content length. No Range or If-Range
+ * headers are allowed in the request.
+ *
+ * Second call will require Range and If-Range in the request headers, and
+ * will respond with the range requested.
+ */
+function handleRequest(request, response)
+{
+  // Set http version for error responses.
+  gHttpVersion = request.httpVersion;
+
+  // All responses, inc. errors, are text/html.
+  response.setHeader("Content-Type", "text/html; charset=UTF-8", false);
+
+  // Get state var to determine if this is the first or second request.
+  var expectedRequestType;
+  if (getState("expectedRequestType") === "") {
+    DBG("First call: Should be requesting full content.");
+    expectedRequestType = "fullRequest";
+    // Set state var for second request.
+    setState("expectedRequestType", "partialRequest");
+  } else if (getState("expectedRequestType") === "partialRequest") {
+    DBG("Second call: Should be requesting undelivered content.");
+    expectedRequestType = "partialRequest";
+    // Reset state var for first request.
+    setState("expectedRequestType", "");
+  } else {
+    ERR(response, 500, "Internal Server Error",
+        "Invalid expectedRequestType \"" + expectedRequestType + "\"in " +
+        "server state db.");
+    return;
+  }
+
+  // Look for Range and If-Range
+  var range = request.hasHeader("Range") ? request.getHeader("Range") : "";
+  var ifRange = request.hasHeader("If-Range") ? request.getHeader("If-Range") : "";
+
+  if (expectedRequestType === "fullRequest") {
+    // Should not have Range or If-Range in first request.
+    if (range && range.length > 0) {
+      ERR(response, 400, "Bad Request",
+          "Should not receive \"Range: " + range + "\" for first, full request.");
+      return;
+    }
+    if (ifRange && ifRange.length > 0) {
+      ERR(response, 400, "Bad Request",
+          "Should not receive \"Range: " + range + "\" for first, full request.");
+      return;
+    }
+  } else if (expectedRequestType === "partialRequest") {
+    // Range AND If-Range should both be present in second request.
+    if (!range) {
+      ERR(response, 400, "Bad Request",
+          "Should receive \"Range: \" for second, partial request.");
+      return;
+    }
+    if (!ifRange) {
+      ERR(response, 400, "Bad Request",
+          "Should receive \"If-Range: \" for second, partial request.");
+      return;
+    }
+  } else {
+    // Somewhat redundant, but a check for errors in this test code.
+    ERR(response, 500, "Internal Server Error",
+        "expectedRequestType not set correctly: \"" + expectedRequestType + "\"");
+    return;
+  }
+
+  // Prepare content in two parts for responses.
+  var partialContent = "<html><head></head><body><p id=\"firstResponse\">" +
+                       "First response</p>";
+  var remainderContent = "<p id=\"secondResponse\">Second response</p>" +
+                         "</body></html>";
+  var totalLength = partialContent.length + remainderContent.length;
+
+  DBG("totalLength: " + totalLength);
+
+  // Prepare common headers for the two responses.
+  response.setHeader("ETag", "abcd0123", false);
+  response.setHeader("Accept-Ranges", "bytes", false);
+
+  // Prepare specific headers and content for first and second responses.
+  if (expectedRequestType === "fullRequest") {
+    DBG("First response: Sending partial content with a full header");
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.write(partialContent, partialContent.length);
+    // Set Content-Length to full length of resource.
+    response.setHeader("Content-Length", "" + totalLength, false);
+  } else if (expectedRequestType === "partialRequest") {
+    DBG("Second response: Sending remaining content with a range header");
+    response.setStatusLine(request.httpVersion, 206, "Partial Content");
+    response.setHeader("Content-Range", "bytes " + partialContent.length + "-" +
+                       (totalLength - 1) + "/" + totalLength);
+    response.write(remainderContent);
+    // Set Content-Length to length of bytes transmitted.
+    response.setHeader("Content-Length", "" + remainderContent.length, false);
+  } else {
+    // Somewhat redundant, but a check for errors in this test code.
+    ERR(response, 500, "Internal Server Error",
+       "Something very bad happened here: expectedRequestType is invalid " +
+       "towards the end of handleRequest! - \"" + expectedRequestType + "\"");
+    return;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/mochitests/test_partially_cached_content.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+  https://bugzilla.mozilla.org/show_bug.cgi?id=497003
+
+  This test verifies that partially cached content is read from the cache first
+  and then from the network. It is written in the mochitest framework to take
+  thread retargeting into consideration of nsIStreamListener callbacks (inc.
+  nsIRequestObserver). E.g. HTML5 Stream Parser requesting retargeting of
+  nsIStreamListener callbacks to the parser thread.
+-->
+<head>
+  <meta charset="UTF-8">
+  <title>Test for Bug 497003: support sending OnDataAvailable() to other threads</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=497003">Mozilla Bug 497003: support sending OnDataAvailable() to other threads</a></p>
+  <p><iframe id="contentFrame" src="partial_content.sjs"></iframe></p>
+
+<pre id="test">
+<script>
+
+
+
+/* Check that the iframe has initial content only after the first load.
+ */
+function expectInitialContent(e) {
+  info("expectInitialContent",
+       "First response received: should have partial content");
+  var frameWindow = document.getElementById('contentFrame').contentWindow;
+
+  // Expect "First response" in received HTML.
+  var firstResponse = frameWindow.document.getElementById('firstResponse');
+  is(firstResponse.innerHTML, "First response", "firstResponse");
+
+  // Expect NOT to get any second response element.
+  var secondResponse = frameWindow.document.getElementById('secondResponse');
+  ok(!secondResponse, "Should not get text for second response in first.");
+
+  // Set up listener for second load.
+  e.target.removeEventListener("load", expectInitialContent, false);
+  e.target.addEventListener("load", expectFullContent, false);
+
+  // Reload.
+  e.target.src="partial_content.sjs";
+}
+
+/* Check that the iframe has all the content after the second load.
+ */
+function expectFullContent(e)
+{
+  info("expectFullContent",
+       "Second response received: should complete content from first load");
+  var frameWindow = document.getElementById('contentFrame').contentWindow;
+
+  // Expect "First response" to still be there
+  var firstResponse = frameWindow.document.getElementById('firstResponse');
+  is(firstResponse.innerHTML, "First response", "firstResponse");
+
+  // Expect "Second response" to be there also.
+  var secondResponse = frameWindow.document.getElementById('secondResponse');
+  is(secondResponse.innerHTML, "Second response", "secondResponse");
+
+  SimpleTest.finish();
+}
+
+// Set listener for first load to expect partial content.
+document.getElementById('contentFrame')
+  .addEventListener("load", expectInitialContent, false);
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['httpserver', 'browser']
+TEST_DIRS += ['httpserver', 'browser', 'mochitests']
 
 MODULE = 'test_necko'
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X
 if CONFIG['OS_ARCH'] != 'Darwin':
     XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -21,16 +21,18 @@
 #include "nsIScriptError.h"
 #include "mozilla/Preferences.h"
 #include "nsHtml5Highlighter.h"
 #include "expat_config.h"
 #include "expat.h"
 #include "nsINestedURI.h"
 #include "nsCharsetSource.h"
 #include "nsIWyciwygChannel.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "nsPrintfCString.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 
 using namespace mozilla;
 using mozilla::dom::EncodingUtils;
 
 int32_t nsHtml5StreamParser::sTimerInitialDelay = 120;
 int32_t nsHtml5StreamParser::sTimerSubsequentDelay = 120;
@@ -68,19 +70,20 @@ nsHtml5StreamParser::InitializeStatics()
  * posted from the parser thread to main thread don't need to wrap any
  * runnable-specific data. (In the other direction, the runnables most notably
  * wrap the byte data of the stream.)
  */
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5StreamParser)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5StreamParser)
 
 NS_INTERFACE_TABLE_HEAD(nsHtml5StreamParser)
-  NS_INTERFACE_TABLE2(nsHtml5StreamParser, 
+  NS_INTERFACE_TABLE3(nsHtml5StreamParser,
                       nsIStreamListener, 
-                      nsICharsetDetectionObserver)
+                      nsICharsetDetectionObserver,
+                      nsIThreadRetargetableStreamListener)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5StreamParser)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5StreamParser)
   tmp->DropTimer();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObserver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
@@ -921,16 +924,24 @@ nsHtml5StreamParser::OnStartRequest(nsIR
     // XXX does Necko have a way to renavigate POST, etc. without hitting
     // the network?
     if (!method.EqualsLiteral("GET")) {
       // This is the old Gecko behavior but the HTML5 spec disagrees.
       // Don't reparse on POST.
       mReparseForbidden = true;
       mFeedChardet = false; // can't restart anyway
     }
+
+    // Attempt to retarget delivery of data (via OnDataAvailable) to the parser
+    // thread, rather than through the main thread.
+    nsCOMPtr<nsIThreadRetargetableRequest> threadRetargetableRequest =
+      do_QueryInterface(mRequest);
+    if (threadRetargetableRequest) {
+      threadRetargetableRequest->RetargetDeliveryTo(mThread);
+    }
   }
 
   if (mCharsetSource == kCharsetFromParentFrame) {
     // Remember this in case chardet overwrites mCharsetSource
     mInitialEncodingWasFromParentFrame = true;
   }
 
   if (mCharsetSource >= kCharsetFromAutoDetection) {
@@ -955,16 +966,32 @@ nsHtml5StreamParser::OnStartRequest(nsIR
   // if we failed to get a decoder, there will be fallback, so don't propagate
   //  the error.
   if (NS_FAILED(rv)) {
     mCharsetSource = kCharsetFromWeakDocTypeDefault;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHtml5StreamParser::CheckListenerChain()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
+  if (!mObserver) {
+    return NS_OK;
+  }
+  nsresult rv;
+  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
+    do_QueryInterface(mObserver, &rv);
+  if (NS_SUCCEEDED(rv) && retargetable) {
+    rv = retargetable->CheckListenerChain();
+  }
+  return rv;
+}
+
 void
 nsHtml5StreamParser::DoStopRequest()
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
                   "Stream ended without being open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
@@ -1008,29 +1035,34 @@ class nsHtml5RequestStopper : public nsR
 };
 
 nsresult
 nsHtml5StreamParser::OnStopRequest(nsIRequest* aRequest,
                              nsISupports* aContext,
                              nsresult status)
 {
   NS_ASSERTION(mRequest == aRequest, "Got Stop on wrong stream.");
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread() || IsParserThread(), "Wrong thread!");
   if (mObserver) {
     mObserver->OnStopRequest(aRequest, aContext, status);
   }
-  nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
-  if (NS_FAILED(mThread->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
-    NS_WARNING("Dispatching StopRequest event failed.");
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
+    if (NS_FAILED(mThread->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
+      NS_WARNING("Dispatching StopRequest event failed.");
+    }
+  } else {
+    mozilla::MutexAutoLock autoLock(mTokenizerMutex);
+    DoStopRequest();
   }
   return NS_OK;
 }
 
 void
-nsHtml5StreamParser::DoDataAvailable(uint8_t* aBuffer, uint32_t aLength)
+nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength)
 {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   NS_PRECONDITION(STREAM_BEING_READ == mStreamState,
                   "DoDataAvailable called when stream not open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   if (IsTerminated()) {
     return;
@@ -1105,34 +1137,68 @@ nsHtml5StreamParser::OnDataAvailable(nsI
 {
   nsresult rv;
   if (NS_FAILED(rv = mExecutor->IsBroken())) {
     return rv;
   }
 
   NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
   uint32_t totalRead;
-  const mozilla::fallible_t fallible = mozilla::fallible_t();
-  nsAutoArrayPtr<uint8_t> data(new (fallible) uint8_t[aLength]);
-  if (!data) {
-    return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
+  // Main thread to parser thread dispatch requires copying to buffer first.
+  if (NS_IsMainThread()) {
+    const mozilla::fallible_t fallible = mozilla::fallible_t();
+    nsAutoArrayPtr<uint8_t> data(new (fallible) uint8_t[aLength]);
+    if (!data) {
+      return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
+    }
+    rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
+                         aLength, &totalRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
+    
+    nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
+                                                                   data.forget(),
+                                                                   totalRead);
+    if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
+      NS_WARNING("Dispatching DataAvailable event failed.");
+    }
+    return rv;
+  } else {
+    NS_ASSERTION(IsParserThread(), "Wrong thread!");
+    mozilla::MutexAutoLock autoLock(mTokenizerMutex);
+
+    // Read directly from response buffer.
+    rv = aInStream->ReadSegments(CopySegmentsToParser, this, aLength,
+                                 &totalRead);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed reading response data to parser");
+      return rv;
+    }
+    return NS_OK;
   }
-  rv = aInStream->Read(reinterpret_cast<char*>(data.get()),
-  aLength, &totalRead);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
-  nsCOMPtr<nsIRunnable> dataAvailable = new nsHtml5DataAvailable(this,
-                                                                 data.forget(),
-                                                                totalRead);
-  if (NS_FAILED(mThread->Dispatch(dataAvailable, nsIThread::DISPATCH_NORMAL))) {
-    NS_WARNING("Dispatching DataAvailable event failed.");
-  }
-  return rv;
 }
 
+/* static */
+NS_METHOD
+nsHtml5StreamParser::CopySegmentsToParser(nsIInputStream *aInStream,
+                                          void *aClosure,
+                                          const char *aFromSegment,
+                                          uint32_t aToOffset,
+                                          uint32_t aCount,
+                                          uint32_t *aWriteCount)
+{
+  nsHtml5StreamParser* parser = static_cast<nsHtml5StreamParser*>(aClosure);
+
+  parser->DoDataAvailable((const uint8_t*)aFromSegment, aCount);
+  // Assume DoDataAvailable consumed all available bytes.
+  *aWriteCount = aCount;
+  return NS_OK;
+}
+
+
 bool
 nsHtml5StreamParser::PreferredForInternalEncodingDecl(nsACString& aEncoding)
 {
   nsAutoCString newEncoding;
   if (!EncodingUtils::FindEncodingForLabel(aEncoding, newEncoding)) {
     // the encoding name is bogus
     mTreeBuilder->MaybeComplainAboutCharset("EncMetaUnsupported",
                                             true,
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -15,16 +15,17 @@
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5OwningUTF16Buffer.h"
 #include "nsIInputStream.h"
 #include "mozilla/Mutex.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Speculation.h"
 #include "nsITimer.h"
 #include "nsICharsetDetector.h"
+#include "nsIThreadRetargetableStreamListener.h"
 
 class nsHtml5Parser;
 
 #define NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE 1024
 #define NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE 1024
 
 enum eParserMode {
   /**
@@ -96,16 +97,17 @@ enum eBomState {
 
 enum eHtml5StreamState {
   STREAM_NOT_STARTED = 0,
   STREAM_BEING_READ = 1,
   STREAM_ENDED = 2
 };
 
 class nsHtml5StreamParser : public nsIStreamListener,
+                            public nsIThreadRetargetableStreamListener,
                             public nsICharsetDetectionObserver {
 
   friend class nsHtml5RequestStopper;
   friend class nsHtml5DataAvailable;
   friend class nsHtml5StreamParserContinuation;
   friend class nsHtml5TimerKungFu;
 
   public:
@@ -120,16 +122,18 @@ class nsHtml5StreamParser : public nsISt
                         eParserMode aMode);
                         
     virtual ~nsHtml5StreamParser();
 
     // nsIRequestObserver methods:
     NS_DECL_NSIREQUESTOBSERVER
     // nsIStreamListener methods:
     NS_DECL_NSISTREAMLISTENER
+    // nsIThreadRetargetableStreamListener methods:
+    NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     
     // nsICharsetDetectionObserver
     /**
      * Chardet calls this to report the detection result
      */
     NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf);
 
     // EncodingDeclarationHandler
@@ -234,17 +238,24 @@ class nsHtml5StreamParser : public nsISt
      * timer.
      */
     void FlushTreeOpsAndDisarmTimer();
 
     void ParseAvailableData();
     
     void DoStopRequest();
     
-    void DoDataAvailable(uint8_t* aBuffer, uint32_t aLength);
+    void DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength);
+
+    static NS_METHOD CopySegmentsToParser(nsIInputStream *aInStream,
+                                          void *aClosure,
+                                          const char *aFromSegment,
+                                          uint32_t aToOffset,
+                                          uint32_t aCount,
+                                          uint32_t *aWriteCount);
 
     bool IsTerminatedOrInterrupted() {
       mozilla::MutexAutoLock autoLock(mTerminatedMutex);
       return mTerminated || mInterrupted;
     }
 
     bool IsTerminated() {
       mozilla::MutexAutoLock autoLock(mTerminatedMutex);
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -223,17 +223,16 @@ class nsHtml5TreeOpExecutor : public nsC
      */
     nsresult MarkAsBroken(nsresult aReason);
 
     /**
      * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0)
      * value if broken.
      */
     inline nsresult IsBroken() {
-      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
       return mBroken;
     }
 
     inline void BeginDocUpdate() {
       NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
       NS_PRECONDITION(mParser, "Started update without parser.");
       mFlushState = eInDocUpdate;
       mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
--- a/python/mozboot/bin/bootstrap.py
+++ b/python/mozboot/bin/bootstrap.py
@@ -30,16 +30,17 @@ from optparse import OptionParser
 # available locally.
 REPOSITORY_PATH_PREFIX = 'python/mozboot'
 
 REPOSITORY_PATHS = [
     'mozboot/__init__.py',
     'mozboot/base.py',
     'mozboot/bootstrap.py',
     'mozboot/centos.py',
+    'mozboot/debian.py',
     'mozboot/fedora.py',
     'mozboot/gentoo.py',
     'mozboot/mint.py',
     'mozboot/openbsd.py',
     'mozboot/osx.py',
     'mozboot/ubuntu.py',
 ]
 
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -4,16 +4,17 @@
 
 # If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
 from __future__ import print_function
 
 import platform
 import sys
 
 from mozboot.centos import CentOSBootstrapper
+from mozboot.debian import DebianBootstrapper
 from mozboot.fedora import FedoraBootstrapper
 from mozboot.gentoo import GentooBootstrapper
 from mozboot.mint import MintBootstrapper
 from mozboot.osx import OSXBootstrapper
 from mozboot.openbsd import OpenBSDBootstrapper
 from mozboot.ubuntu import UbuntuBootstrapper
 
 
@@ -36,16 +37,18 @@ class Bootstrapper(object):
         cls = None
         args = {}
 
         if sys.platform.startswith('linux'):
             distro, version, dist_id = platform.linux_distribution()
 
             if distro == 'CentOS':
                 cls = CentOSBootstrapper
+            elif distro in ('Debian', 'debian'):
+                cls = DebianBootstrapper
             elif distro == 'Fedora':
                 cls = FedoraBootstrapper
             elif distro == 'Gentoo Base System':
                 cls = GentooBootstrapper
             elif distro in ('Mint', 'LinuxMint'):
                 cls = MintBootstrapper
             elif distro == 'Ubuntu':
                 cls = UbuntuBootstrapper
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/debian.py
@@ -0,0 +1,29 @@
+# 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/.
+
+import os
+
+from mozboot.base import BaseBootstrapper
+
+class DebianBootstrapper(BaseBootstrapper):
+    def __init__(self, version, dist_id):
+        BaseBootstrapper.__init__(self)
+
+        self.version = version
+        self.dist_id = dist_id
+
+    def install_system_packages(self):
+        self.run_as_root(['apt-get', 'build-dep', 'iceweasel'])
+
+        self.apt_install(
+            'autoconf2.13',
+            'libasound2-dev',
+            'libcurl4-openssl-dev',
+            'libiw-dev',
+            'libnotify-dev',
+            'libxt-dev',
+            'mercurial',
+            'mesa-common-dev',
+            'uuid',
+            'yasm')
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -16,19 +16,16 @@
 // if available. Using a separate CID allows us to overwrite the XUL
 // alerts service at runtime.
 #define NS_SYSTEMALERTSERVICE_CONTRACTID \
   "@mozilla.org/system-alerts-service;1"
 
 #define NS_AUTOCOMPLETECONTROLLER_CONTRACTID \
   "@mozilla.org/autocomplete/controller;1"
 
-#define NS_AUTOCOMPLETECONTROLLER_CONTRACTID \
-  "@mozilla.org/autocomplete/controller;1"
-
 #define NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID \
   "@mozilla.org/autocomplete/simple-result;1"
 
 #define NS_AUTOCOMPLETEMDBRESULT_CONTRACTID \
   "@mozilla.org/autocomplete/mdb-result;1"
 
 #define NS_DOWNLOADMANAGER_CONTRACTID \
   "@mozilla.org/download-manager;1"
--- a/toolkit/components/prompts/src/nsPrompter.js
+++ b/toolkit/components/prompts/src/nsPrompter.js
@@ -360,23 +360,38 @@ XPCOMUtils.defineLazyGetter(PromptUtils,
         ellipsis = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
     } catch (e) { }
     return ellipsis;
 });
 
 
 
 function openModalWindow(domWin, uri, args) {
-    // XXX Investigate supressing modal state when we're called without a
-    // window? Seems odd to affect whatever window happens to be active.
-    if (!domWin)
+    // There's an implied contract that says modal prompts should still work
+    // when no "parent" window is passed for the dialog (eg, the "Master
+    // Password" dialog does this).  These prompts must be shown even if there
+    // are *no* visible windows at all.
+    // There's also a requirement for prompts to be blocked if a window is
+    // passed and that window is hidden (eg, auth prompts are supressed if the
+    // passed window is the hidden window).
+    // See bug 875157 comment 30 for more...
+    if (domWin) {
+        // a domWin was passed, so we can apply the check for it being hidden.
+        let winUtils = domWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+        if (!winUtils.isParentWindowMainWidgetVisible) {
+            throw Components.Exception("Cannot call openModalWindow on a hidden window",
+                                       Cr.NS_ERROR_NOT_AVAILABLE);
+        }
+    } else {
+        // We try and find a window to use as the parent, but don't consider
+        // if that is visible before showing the prompt.
         domWin = Services.ww.activeWindow;
-
-    // domWin may still be null here if there are _no_ windows open.
-
+        // domWin may still be null here if there are _no_ windows open.
+    }
     // Note that we don't need to fire DOMWillOpenModalDialog and
     // DOMModalDialogClosed events here, wwatcher's OpenWindowInternal
     // will do that. Similarly for enterModalState / leaveModalState.
 
     Services.ww.openWindow(domWin, uri, "_blank", "centerscreen,chrome,modal,titlebar", args);
 }
 
 function openTabPrompt(domWin, tabPrompt, args) {
--- a/toolkit/components/social/FrameWorker.jsm
+++ b/toolkit/components/social/FrameWorker.jsm
@@ -340,18 +340,18 @@ function makeHiddenFrame() {
 
   hiddenDoc.documentElement.appendChild(iframe);
 
   // Disable some types of content
   let docShell = iframe.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell);
   docShell.allowAuth = false;
   docShell.allowPlugins = false;
   docShell.allowImages = false;
+  docShell.allowMedia = false;
   docShell.allowWindowControl = false;
-  // TODO: disable media (bug 759964)
   return iframe;
 }
 
 // public methods on WorkerHandle should conform to the SharedWorker api
 function WorkerHandle(port, worker) {
   this.port = port;
   this._worker = worker;
 }
--- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
+++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
@@ -6,16 +6,23 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 
 const backgroundPageThumbsContent = {
 
   init: function () {
+    // Arrange to prevent (most) popup dialogs for this window - popups done
+    // in the parent (eg, auth) aren't prevented, but alert() etc are.
+    let dwu = content.
+                QueryInterface(Ci.nsIInterfaceRequestor).
+                getInterface(Ci.nsIDOMWindowUtils);
+    dwu.preventFurtherDialogs();
+
     // Stop about:blank from loading.  If it finishes loading after a capture
     // request is received, it could trigger the capture's load listener.
     this._webNav.stop(Ci.nsIWebNavigation.STOP_NETWORK);
     addMessageListener("BackgroundPageThumbs:capture",
                        this._onCapture.bind(this));
   },
 
   get _webNav() {
--- a/toolkit/components/thumbnails/test/browser_thumbnails_background.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_background.js
@@ -222,20 +222,53 @@ let tests = [
     let file = fileForURL(url);
     ok(file.exists(), "Thumbnail file should exist after capture.");
 
     let deferred = imports.Promise.defer();
     retrieveImageDataForURL(url, function ([r, g, b]) {
       isnot([r, g, b].toString(), [0, 255, 0].toString(),
             "The captured page should not be green.");
       gBrowser.removeTab(tab);
+      file.remove(false);
       deferred.resolve();
     });
     yield deferred.promise;
   },
+
+  // the following tests attempt to display modal dialogs.  The test just
+  // relies on the fact that if the dialog was displayed the test will hang
+  // and timeout.  IOW - the tests would pass if the dialogs appear and are
+  // manually closed by the user - so don't do that :)  (obviously there is
+  // noone available to do that when run via tbpl etc, so this should be safe,
+  // and it's tricky to use the window-watcher to check a window *does not*
+  // appear - how long should the watcher be active before assuming it's not
+  // going to appear?)
+  function noAuthPrompt() {
+    let url = "http://mochi.test:8888/browser/browser/base/content/test/authenticate.sjs?user=anyone";
+    let file = fileForURL(url);
+    ok(!file.exists(), "Thumbnail file should not already exist.");
+
+    let capturedURL = yield capture(url);
+    is(capturedURL, url, "Captured URL should be URL passed to capture.");
+    ok(file.exists(),
+       "Thumbnail file should exist even though it requires auth.");
+    file.remove(false);
+  },
+
+  function noAlert() {
+    let url = "data:text/html,<script>alert('yo!');</script>";
+    let file = fileForURL(url);
+    ok(!file.exists(), "Thumbnail file should not already exist.");
+
+    let capturedURL = yield capture(url);
+    is(capturedURL, url, "Captured URL should be URL passed to capture.");
+    ok(file.exists(),
+       "Thumbnail file should exist even though it alerted.");
+    file.remove(false);
+  },
 ];
 
 function capture(url, options) {
   let deferred = imports.Promise.defer();
   options = options || {};
   options.onDone = function onDone(capturedURL) {
     deferred.resolve(capturedURL);
   };
--- a/toolkit/identity/Sandbox.jsm
+++ b/toolkit/identity/Sandbox.jsm
@@ -94,18 +94,18 @@ this.Sandbox.prototype = {
 
     // Stop about:blank from being loaded.
     docShell.stop(Ci.nsIWebNavigation.STOP_NETWORK);
 
     // Disable some types of content
     docShell.allowAuth = false;
     docShell.allowPlugins = false;
     docShell.allowImages = false;
+    docShell.allowMedia = false;
     docShell.allowWindowControl = false;
-    // TODO: disable media (bug 759964)
 
     // Disable stylesheet loading since the document is not visible.
     let markupDocViewer = docShell.contentViewer
                                   .QueryInterface(Ci.nsIMarkupDocumentViewer);
     markupDocViewer.authorStyleDisabled = true;
 
     // Set instance properties.
     this._frame = frame;
--- a/toolkit/identity/tests/chrome/test_sandbox.xul
+++ b/toolkit/identity/tests/chrome/test_sandbox.xul
@@ -28,17 +28,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 SimpleTest.expectAssertions(2);
 
 SimpleTest.waitForExplicitFinish();
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
-const OSX_10_5 = navigator.oscpu == "Intel Mac OS X 10.5";
 
 const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
 const TEST_URL_1 = "https://example.com/";
 // No trailing slash plus port to test normalization
 const TEST_URL_2 = "https://example.com:443";
 
 const TEST_BASE = "http://mochi.test:8888/chrome/toolkit/identity/tests/chrome/"
@@ -126,30 +125,28 @@ function check_loaded_content(aSandbox, 
   let xhr = new XMLHttpRequest();
   xhr.open("GET", STATE_URL + "?get_loaded", true);
   xhr.onload = function() {
     let res = xhr.responseText;
     is(xhr.status, 200, "Check successful response");
 
     if (aNothingShouldLoad) {
       is(res, "NOTHING", "Check that nothing was loaded on the server");
-    } else if (!OSX_10_5) {
-      let allowedTypes = [ "application/javascript", "text/html", "video/webm",
-                           "audio/ogg", "application/x-test" ];
+    } else {
+      let allowedTypes = [ "application/javascript", "text/html", "application/x-test" ];
       let loadedTypes = res == "NOTHING" ? [] : res.split(",");
 
       for (let loadedType of loadedTypes) {
-        isnot(allowedTypes.indexOf(loadedType), -1, "Check that " + loadedType + " was expected to load");
+        isnot(allowedTypes.indexOf(loadedType), -1, "Check that " + loadedType + " was expected to load"); // TODO
       }
 
-      // TODO: Media should be disabled after bug 759964.
       isnot(loadedTypes.indexOf("application/javascript"), -1, "Check JS was loaded");
       isnot(loadedTypes.indexOf("text/html"), -1, "Check iframe was loaded");
-      //todo_is(loadedTypes.indexOf("video/webm"), -1, "Check webm was not loaded");
-      //todo_is(loadedTypes.indexOf("audio/ogg"), -1, "Check ogg was not loaded");
+      is(loadedTypes.indexOf("video/webm"), -1, "Check webm was not loaded");
+      is(loadedTypes.indexOf("audio/ogg"), -1, "Check ogg was not loaded");
 
       // Check that no plugin tags have a type other than TYPE_NULL (failed load)
       // --
       // Checking if a channel was opened is not sufficient for plugin tags --
       // An object tag may still be allowed to load a sub-document, but not a
       // plugin, so it will open a channel but then abort when it gets a
       // plugin-type.
       let doc = aSandbox._frame.contentDocument;
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -259,17 +259,18 @@ PopupNotifications.prototype = {
     } else {
       // Otherwise, update() will display the notification the next time the
       // relevant tab/window is selected.
 
       // If the tab is selected but the window is in the background, let the OS
       // tell the user that there's a notification waiting in that window.
       // At some point we might want to do something about background tabs here
       // too.
-      if (browser == this.tabbrowser.selectedBrowser)
+      if (!notification.dismissed &&
+          browser == this.tabbrowser.selectedBrowser)
         this.window.getAttention();
 
       // Notify observers that we're not showing the popup (useful for testing)
       this._notify("backgroundShow");
     }
 
     return notification;
   },
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sts=2 sw=2 et cin: */
 /* 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 "nsURILoader.h"
 #include "nsAutoPtr.h"
+#include "nsProxyRelease.h"
 #include "nsIURIContentListener.h"
 #include "nsIContentHandler.h"
 #include "nsILoadGroup.h"
 #include "nsIDocumentLoader.h"
 #include "nsIWebProgress.h"
 #include "nsIWebProgressListener.h"
 #include "nsIIOService.h"
 #include "nsIServiceManager.h"
@@ -25,20 +26,22 @@
 #include "nsWeakReference.h"
 #include "nsIHttpChannel.h"
 #include "nsIMultiPartChannel.h"
 #include "netCore.h"
 #include "nsCRT.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
+#include "nsIThreadRetargetableStreamListener.h"
 
 #include "nsXPIDLString.h"
 #include "nsString.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
 #include "nsReadableUtils.h"
 #include "nsError.h"
 
 #include "nsICategoryManager.h"
 #include "nsCExternalHandlerService.h" // contains contractids for the helper app service
 
 #include "nsIMIMEHeaderParam.h"
 #include "nsNetCID.h"
@@ -58,16 +61,17 @@ PRLogModuleInfo* nsURILoader::mLog = nul
 
 /**
  * The nsDocumentOpenInfo contains the state required when a single
  * document is being opened in order to discover the content type...
  * Each instance remains alive until its target URL has been loaded
  * (or aborted).
  */
 class nsDocumentOpenInfo MOZ_FINAL : public nsIStreamListener
+                                   , public nsIThreadRetargetableStreamListener
 {
 public:
   // Needed for nsCOMPtr to work right... Don't call this!
   nsDocumentOpenInfo();
 
   // Real constructor
   // aFlags is a combination of the flags on nsIURILoader
   nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
@@ -105,31 +109,33 @@ public:
                             nsIChannel* aChannel);
 
   // nsIRequestObserver methods:
   NS_DECL_NSIREQUESTOBSERVER
 
   // nsIStreamListener methods:
   NS_DECL_NSISTREAMLISTENER
 
+  // nsIThreadRetargetableStreamListener
+  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 protected:
   ~nsDocumentOpenInfo();
 
 protected:
   /**
    * The first content listener to try dispatching data to.  Typically
    * the listener associated with the entity that originated the load.
    */
   nsCOMPtr<nsIURIContentListener> m_contentListener;
 
   /**
    * The stream listener to forward nsIStreamListener notifications
    * to.  This is set once the load is dispatched.
    */
-  nsCOMPtr<nsIStreamListener> m_targetStreamListener;
+  nsMainThreadPtrHandle<nsIStreamListener> m_targetStreamListener;
 
   /**
    * A pointer to the entity that originated the load. We depend on getting
    * things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
    */
   nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
 
   /**
@@ -154,16 +160,17 @@ protected:
 
 NS_IMPL_THREADSAFE_ADDREF(nsDocumentOpenInfo)
 NS_IMPL_THREADSAFE_RELEASE(nsDocumentOpenInfo)
 
 NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 nsDocumentOpenInfo::nsDocumentOpenInfo()
 {
   NS_NOTREACHED("This should never be called\n");
 }
 
 nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
@@ -262,16 +269,32 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStar
     rv = m_targetStreamListener->OnStartRequest(request, aCtxt);
 
   LOG(("  OnStartRequest returning: 0x%08X", rv));
   
   return rv;
 }
 
 NS_IMETHODIMP
+nsDocumentOpenInfo::CheckListenerChain()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+    do_QueryInterface(m_targetStreamListener, &rv);
+  if (retargetableListener) {
+    rv = retargetableListener->CheckListenerChain();
+  }
+  LOG(("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv %x",
+       this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
+       (nsIStreamListener*)m_targetStreamListener, rv));
+  return rv;
+}
+
+NS_IMETHODIMP
 nsDocumentOpenInfo::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
                                     nsIInputStream * inStr,
                                     uint64_t sourceOffset, uint32_t count)
 {
   // if we have retarged to the end stream listener, then forward the call....
   // otherwise, don't do anything
 
   nsresult rv = NS_OK;
@@ -283,17 +306,17 @@ nsDocumentOpenInfo::OnDataAvailable(nsIR
 
 NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest *request, nsISupports *aCtxt, 
                                                 nsresult aStatus)
 {
   LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
   
   if ( m_targetStreamListener)
   {
-    nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
+    nsMainThreadPtrHandle<nsIStreamListener> listener = m_targetStreamListener;
 
     // If this is a multipart stream, we could get another
     // OnStartRequest after this... reset state.
     m_targetStreamListener = 0;
     mContentType.Truncate();
     listener->OnStopRequest(request, aCtxt, aStatus);
   }
 
@@ -509,21 +532,25 @@ nsresult nsDocumentOpenInfo::DispatchCon
     request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI
                                     | nsIChannel::LOAD_TARGETED);
 
     if (isGuessFromExt) {
       mContentType = APPLICATION_GUESS_FROM_EXT;
       aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
     }
 
+    nsCOMPtr<nsIStreamListener> listener;
     rv = helperAppService->DoContent(mContentType,
                                      request,
                                      m_originalContext,
                                      false,
-                                     getter_AddRefs(m_targetStreamListener));
+                                     getter_AddRefs(listener));
+    // Passing false here to allow off main thread use.
+    m_targetStreamListener
+      = new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
     if (NS_FAILED(rv)) {
       request->SetLoadFlags(loadFlags);
       m_targetStreamListener = nullptr;
     }
   }
       
   NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
                "There is no way we should be successful at this point without a m_targetStreamListener");
@@ -553,17 +580,17 @@ nsDocumentOpenInfo::ConvertData(nsIReque
   // When applying stream decoders, it is necessary to "insert" an 
   // intermediate nsDocumentOpenInfo instance to handle the targeting of
   // the "final" stream or streams.
   //
   // For certain content types (ie. multi-part/x-mixed-replace) the input
   // stream is split up into multiple destination streams.  This
   // intermediate instance is used to target these "decoded" streams...
   //
-  nsCOMPtr<nsDocumentOpenInfo> nextLink =
+  nsRefPtr<nsDocumentOpenInfo> nextLink =
     new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
   if (!nextLink) return NS_ERROR_OUT_OF_MEMORY;
 
   LOG(("  Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
   
   // Make sure nextLink starts with the contentListener that said it wanted the
   // results of this decode.
   nextLink->m_contentListener = aListener;
@@ -576,21 +603,26 @@ nsDocumentOpenInfo::ConvertData(nsIReque
   // */*, that's OK -- that will just indicate to nextLink that it should get
   // the type off the channel.
   nextLink->mContentType = aOutContentType;
 
   // The following call sets m_targetStreamListener to the input end of the
   // stream converter and sets the output end of the stream converter to
   // nextLink.  As we pump data into m_targetStreamListener the stream
   // converter will convert it and pass the converted data to nextLink.
-  return StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(), 
-                                             PromiseFlatCString(aOutContentType).get(), 
-                                             nextLink, 
-                                             request,
-                                             getter_AddRefs(m_targetStreamListener));
+  nsCOMPtr<nsIStreamListener> listener;
+  rv = StreamConvService->AsyncConvertData(PromiseFlatCString(aSrcContentType).get(),
+                                           PromiseFlatCString(aOutContentType).get(),
+                                           nextLink,
+                                           request,
+                                           getter_AddRefs(listener));
+  // Passing false here to allow off main thread use.
+  m_targetStreamListener
+    = new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
+  return rv;
 }
 
 bool
 nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
                                        nsIChannel* aChannel)
 {
   LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x",
        this, mFlags));
@@ -625,17 +657,17 @@ nsDocumentOpenInfo::TryContentListener(n
       m_targetStreamListener = nullptr;
     }
 
     LOG(("  Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
     
     // m_targetStreamListener is now the input end of the converter, and we can
     // just pump the data in there, if it exists.  If it does not, we need to
     // try other nsIURIContentListeners.
-    return m_targetStreamListener != nullptr;
+    return m_targetStreamListener.get() != nullptr;
   }
 
   // At this point, aListener wants data of type mContentType.  Let 'em have
   // it.  But first, if we are retargeting, set an appropriate flag on the
   // channel
   nsLoadFlags loadFlags = 0;
   aChannel->GetLoadFlags(&loadFlags);
 
@@ -647,22 +679,25 @@ nsDocumentOpenInfo::TryContentListener(n
     do_GetInterface(m_originalContext);
   if (originalListener != aListener) {
     newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
   }
   aChannel->SetLoadFlags(loadFlags | newLoadFlags);
   
   bool abort = false;
   bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
+  nsCOMPtr<nsIStreamListener> listener;
   nsresult rv = aListener->DoContent(mContentType.get(),
                                      isPreferred,
                                      aChannel,
-                                     getter_AddRefs(m_targetStreamListener),
+                                     getter_AddRefs(listener),
                                      &abort);
-    
+  // Passing false here to allow off main thread use.
+  m_targetStreamListener
+    = new nsMainThreadPtrHolder<nsIStreamListener>(listener, false);
   if (NS_FAILED(rv)) {
     LOG_ERROR(("  DoContent failed"));
     
     // Unset the RETARGETED_DOCUMENT_URI flag if we set it...
     aChannel->SetLoadFlags(loadFlags);
     m_targetStreamListener = nullptr;
     return false;
   }
@@ -807,17 +842,17 @@ nsresult nsURILoader::OpenChannel(nsICha
         LOG(("  OnStartURIOpen aborted load"));
         return NS_ERROR_WONT_HANDLE_CONTENT;
       }
     }
   }
 
   // we need to create a DocumentOpenInfo object which will go ahead and open
   // the url and discover the content type....
-  nsCOMPtr<nsDocumentOpenInfo> loader =
+  nsRefPtr<nsDocumentOpenInfo> loader =
     new nsDocumentOpenInfo(aWindowContext, aFlags, this);
 
   if (!loader) return NS_ERROR_OUT_OF_MEMORY;
 
   // Set the correct loadgroup on the channel
   nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
 
   if (!loadGroup) {
--- a/widget/LookAndFeel.h
+++ b/widget/LookAndFeel.h
@@ -381,17 +381,18 @@ public:
   enum WindowsTheme {
     eWindowsTheme_Generic = 0, // unrecognized theme
     eWindowsTheme_Classic,
     eWindowsTheme_Aero,
     eWindowsTheme_LunaBlue,
     eWindowsTheme_LunaOlive,
     eWindowsTheme_LunaSilver,
     eWindowsTheme_Royale,
-    eWindowsTheme_Zune
+    eWindowsTheme_Zune,
+    eWindowsTheme_AeroLite
   };
 
   enum {
     eScrollArrow_None = 0,
     eScrollArrow_StartBackward = 0x1000,
     eScrollArrow_StartForward = 0x0100,
     eScrollArrow_EndBackward = 0x0010,
     eScrollArrow_EndForward = 0x0001
--- a/widget/windows/nsUXThemeData.cpp
+++ b/widget/windows/nsUXThemeData.cpp
@@ -264,16 +264,17 @@ nsUXThemeData::UpdateTitlebarInfo(HWND a
 
 struct THEMELIST {
   LPCWSTR name;
   int type;
 };
 
 const THEMELIST knownThemes[] = {
   { L"aero.msstyles", WINTHEME_AERO },
+  { L"aerolite.msstyles", WINTHEME_AERO_LITE },
   { L"luna.msstyles", WINTHEME_LUNA },
   { L"zune.msstyles", WINTHEME_ZUNE },
   { L"royale.msstyles", WINTHEME_ROYALE }
 };
 
 const THEMELIST knownColors[] = {
   { L"normalcolor", WINTHEMECOLOR_NORMAL },
   { L"homestead",   WINTHEMECOLOR_HOMESTEAD },
@@ -335,24 +336,27 @@ nsUXThemeData::UpdateNativeThemeInfo()
       theme = (WindowsTheme)knownThemes[i].type;
       break;
     }
   }
 
   if (theme == WINTHEME_UNRECOGNIZED)
     return;
 
-  if (theme == WINTHEME_AERO || theme == WINTHEME_LUNA)
+  if (theme == WINTHEME_AERO || theme == WINTHEME_AERO_LITE || theme == WINTHEME_LUNA)
     sIsDefaultWindowsTheme = true;
   
   if (theme != WINTHEME_LUNA) {
     switch(theme) {
       case WINTHEME_AERO:
         sThemeId = LookAndFeel::eWindowsTheme_Aero;
         return;
+      case WINTHEME_AERO_LITE:
+        sThemeId = LookAndFeel::eWindowsTheme_AeroLite;
+        return;
       case WINTHEME_ZUNE:
         sThemeId = LookAndFeel::eWindowsTheme_Zune;
         return;
       case WINTHEME_ROYALE:
         sThemeId = LookAndFeel::eWindowsTheme_Royale;
         return;
       default:
         NS_WARNING("unhandled theme type.");
--- a/widget/windows/nsUXThemeData.h
+++ b/widget/windows/nsUXThemeData.h
@@ -57,17 +57,18 @@ enum nsUXThemeClass {
 
 // Native windows style constants
 enum WindowsTheme {
   WINTHEME_UNRECOGNIZED = 0,
   WINTHEME_CLASSIC      = 1, // no theme
   WINTHEME_AERO         = 2,
   WINTHEME_LUNA         = 3,
   WINTHEME_ROYALE       = 4,
-  WINTHEME_ZUNE         = 5
+  WINTHEME_ZUNE         = 5,
+  WINTHEME_AERO_LITE    = 6
 };
 enum WindowsThemeColor {
   WINTHEMECOLOR_UNRECOGNIZED = 0,
   WINTHEMECOLOR_NORMAL       = 1,
   WINTHEMECOLOR_HOMESTEAD    = 2,
   WINTHEMECOLOR_METALLIC     = 3
 };
 
--- a/xpcom/glue/nsProxyRelease.h
+++ b/xpcom/glue/nsProxyRelease.h
@@ -198,16 +198,18 @@ class nsMainThreadPtrHandle
       return mPtr.get()->get();
     }
     return nullptr;
   }
 
   operator T*() { return get(); }
   T* operator->() { return get(); }
 
+  operator bool() { return get(); }
+
   // These are safe to call on other threads with appropriate external locking.
   bool operator==(const nsMainThreadPtrHandle<T>& aOther) const {
     if (!mPtr || !aOther.mPtr)
       return mPtr == aOther.mPtr;
     return *mPtr == *aOther.mPtr;
   }
   bool operator!() { return !mPtr; }
 };