merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 22 Sep 2015 12:35:13 +0200
changeset 263710 a1ccea59e254a88f7bb44b0ad8a58b77b7eca339
parent 263709 f4f27148d24cc676277fca16cc07c1dfaec1461c (current diff)
parent 263636 1056ac371c0468cde5600a7c5765dc170c5f77e5 (diff)
child 263711 9a5faeebdcb584070c4c855804a01c3fd6d43110
child 263813 bd660fbaa412aafae28d0e85e51ea2d3583b9c43
child 263835 7a85eae1973ebefc8b99a14f5b47e8c4541af9ee
push id65396
push usercbook@mozilla.com
push dateTue, 22 Sep 2015 10:50:42 +0000
treeherdermozilla-inbound@9a5faeebdcb5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.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 mozilla-inbound to mozilla-central a=merge
devtools/server/actors/styleeditor.js
devtools/server/actors/stylesheets.js
devtools/shared/gcli/commands/screenshot.js
gfx/layers/apz/reftests/async-scrollbar-1-h-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-h-rtl-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-h-rtl.html
gfx/layers/apz/reftests/async-scrollbar-1-h.html
gfx/layers/apz/reftests/async-scrollbar-1-v-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-v-rtl-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-v-rtl.html
gfx/layers/apz/reftests/async-scrollbar-1-v.html
gfx/layers/apz/reftests/async-scrollbar-1-vh-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl-ref.html
gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl.html
gfx/layers/apz/reftests/async-scrollbar-1-vh.html
gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html
gfx/layers/apz/reftests/async-scrollbar-zoom-1.html
gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html
gfx/layers/apz/reftests/async-scrollbar-zoom-2.html
gfx/layers/apz/reftests/reftest.list
gfx/layers/apz/test/apz_test_native_event_utils.js
gfx/layers/apz/test/apz_test_utils.js
gfx/layers/apz/test/chrome.ini
gfx/layers/apz/test/helper_basic_pan.html
gfx/layers/apz/test/helper_bug1151663.html
gfx/layers/apz/test/helper_bug982141.html
gfx/layers/apz/test/helper_div_pan.html
gfx/layers/apz/test/helper_iframe1.html
gfx/layers/apz/test/helper_iframe2.html
gfx/layers/apz/test/helper_iframe_pan.html
gfx/layers/apz/test/helper_subframe_style.css
gfx/layers/apz/test/mochitest.ini
gfx/layers/apz/test/test_basic_pan.html
gfx/layers/apz/test/test_bug1151663.html
gfx/layers/apz/test/test_bug1151667.html
gfx/layers/apz/test/test_bug982141.html
gfx/layers/apz/test/test_layerization.html
gfx/layers/apz/test/test_scroll_inactive_flattened_frame.html
gfx/layers/apz/test/test_smoothness.html
gfx/layers/apz/test/test_wheel_scroll.html
gfx/layers/apz/test/test_wheel_transactions.html
gfx/tests/gtest/TestAsyncPanZoomController.cpp
layout/reftests/box-shadow/boxshadow-color-rounding-middle-ref.html
layout/reftests/box-shadow/boxshadow-color-rounding-middle.html
modules/libpref/init/all.js
testing/marionette/driver.js
testing/web-platform/meta/content-security-policy/style-src/style-src-3_4.html.ini
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1343,22 +1343,22 @@ DocAccessible::ProcessInvalidationList()
     }
   }
 
   mInvalidationList.Clear();
 
   // Alter the tree according to aria-owns (seize the trees).
   for (uint32_t idx = 0; idx < mARIAOwnsInvalidationList.Length(); idx++) {
     Accessible* owner = mARIAOwnsInvalidationList[idx].mOwner;
-    if (owner->IsDefunct()) { // eventually died until we've got here
+    if (!owner->IsInDocument()) { // eventually died before we've got here
       continue;
     }
 
     Accessible* child = GetAccessible(mARIAOwnsInvalidationList[idx].mChild);
-    if (!child) {
+    if (!child || !child->IsInDocument()) {
       continue;
     }
 
     // XXX: update context flags
     {
       Accessible* oldParent = child->Parent();
       nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
       nsRefPtr<AccMutationEvent> hideEvent =
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -949,17 +949,17 @@ pref("dom.mozInputMethod.enabled", true)
 // Absolute path to the devtool unix domain socket file used
 // to communicate with a usb cable via adb forward
 pref("devtools.debugger.unix-domain-socket", "/data/local/debugger-socket");
 
 // enable Skia/GL (OpenGL-accelerated 2D drawing) for large enough 2d canvases,
 // falling back to Skia/software for smaller canvases
 #ifdef MOZ_WIDGET_GONK
 pref("gfx.canvas.azure.backends", "skia");
-pref("gfx.canvas.azure.accelerated", true);
+pref("gfx.canvas.azure.accelerated", false);
 #endif
 
 // Turn on dynamic cache size for Skia
 pref("gfx.canvas.skiagl.dynamic-cache", true);
 
 // Limit skia to canvases the size of the device screen or smaller
 pref("gfx.canvas.max-size-for-skia-gl", -1);
 
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ b/browser/components/uitour/test/browser_UITour_loop.js
@@ -40,17 +40,17 @@ var tests = [
     gContentAPI.registerPageID("hello-tour_OpenPanel_testPage");
     yield new Promise(resolve => {
       gContentAPI.ping(() => resolve());
     });
 
     let loopDoc = document.getElementById("loop-notification-panel").children[0].contentDocument;
     yield waitForConditionPromise(() => {
       return loopDoc.readyState == 'complete';
-    }, "Loop notification panel document should be fully loaded.");
+    }, "Loop notification panel document should be fully loaded.", 50);
     let gettingStartedButton = loopDoc.getElementById("fte-button");
     ok(gettingStartedButton, "Getting Started button should be found");
 
     let newTabPromise = waitForConditionPromise(() => {
       return gBrowser.currentURI.path.contains("utm_source=firefox-browser");
     }, "New tab with utm_content=testPageNewID should have opened");
 
     gettingStartedButton.click();
--- a/browser/modules/ContentLinkHandler.jsm
+++ b/browser/modules/ContentLinkHandler.jsm
@@ -159,17 +159,17 @@ this.ContentLinkHandler = {
     try {
       var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"].
                           getService(Ci.nsIContentPolicy);
     } catch(e) {
       return null; // Refuse to load if we can't do a security check.
     }
 
     // Security says okay, now ask content policy
-    if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
+    if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE,
                                  uri, targetDoc.documentURIObject,
                                  aLink, aLink.type, null)
                                  != Ci.nsIContentPolicy.ACCEPT)
       return null;
 
     try {
       uri.userPass = "";
     } catch(e) {
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -74,17 +74,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 // nsIURI -> imgIContainer
 function _imageFromURI(doc, uri, privateMode, callback) {
   let channel = ioSvc.newChannelFromURI2(uri,
                                          doc,
                                          null,  // aLoadingPrincipal
                                          null,  // aTriggeringPrincipal
                                          Ci.nsILoadInfo.SEC_NORMAL,
-                                         Ci.nsIContentPolicy.TYPE_IMAGE);
+                                         Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE);
   try {
     channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
     channel.setPrivate(privateMode);
   } catch (e) {
     // Ignore channels which do not support nsIPrivateBrowsingChannel
   }
   NetUtil.asyncFetch(channel, function(inputStream, resultCode) {
     if (!Components.isSuccessCode(resultCode))
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -49,19 +49,16 @@
 menuitem[cmd="cmd_clearhistory"] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
 }
 
 menuitem[cmd="cmd_clearhistory"][disabled] {
   list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
 }
 
-
-
-
 .searchbar-search-button-container {
   -moz-box-align: center;
 }
 
 .searchbar-search-button {
   list-style-image: url("chrome://browser/skin/search-indicator.png");
   -moz-image-region: rect(0, 20px, 20px, 0);
   margin-top: 1px;
@@ -83,66 +80,65 @@ menuitem[cmd="cmd_clearhistory"][disable
 
 
 .search-panel-current-engine {
   border-top: none !important;
   -moz-box-align: center;
 }
 
 .search-panel-current-engine {
-  border-bottom: 1px solid #ccc;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
 }
 
 .search-panel-header {
   font-weight: normal;
-  background-color: rgb(245, 245, 245);
-  border-top: 1px solid #ccc;
+  border-top: 1px solid rgba(0, 0, 0, 0.2);
   padding: 3px 5px;
-  color: #666;
+  color: MenuText;
 }
 
 .search-panel-tree[collapsed=true] + .search-panel-header {
   border-top: none;
 }
 
 .search-panel-header > label {
   margin-top: 2px !important;
   margin-bottom: 1px !important;
 }
 
 .search-panel-current-input > label {
   margin: 2px 0 1px !important;
 }
 
 .search-panel-input-value {
-  color: black;
+  color: Highlight;
 }
 
 .search-panel-one-offs {
   margin: 0 -1px !important;
-  border-top: 1px solid #ccc;
+  border-top: 1px solid rgba(0, 0, 0, 0.2);
 }
 
 .searchbar-engine-one-off-item {
   -moz-appearance: none;
   display: inline-block;
   border: none;
   min-width: 48px;
   height: 32px;
   margin: 0 0;
   padding: 0 0;
   background: none;
-  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gofECQNNVW2/AAAABBJREFUGFdjOHPmzH8GehEA/KpKg9YTf4AAAAAASUVORK5CYII=');
+  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAWCAYAAAABxvaqAAAABmJLR0QA/wD/AP+gvaeTAAAAFElEQVQI12NgYGAwZmJgYGCgBwEAHpIAXs+dj/cAAAAASUVORK5CYII=');
   background-repeat: no-repeat;
   background-position: right center;
 }
 
 .searchbar-engine-one-off-item:not(.last-row) {
   box-sizing: content-box;
-  border-bottom: 1px solid #ccc;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
 }
 
 .searchbar-engine-one-off-item.last-of-row {
   background-image: none;
 }
 
 .searchbar-engine-one-off-item[selected] {
   background-color: Highlight;
@@ -173,17 +169,17 @@ menuitem[cmd="cmd_clearhistory"][disable
   padding: 0 10px;
 }
 
 .addengine-item > .button-box {
   -moz-box-pack: start;
 }
 
 .addengine-item:first-of-type {
-  border-top: 1px solid #ccc;
+  border-top: 1px solid rgba(0, 0, 0, 0.2);
 }
 
 .addengine-item[selected] {
   background-color: Highlight;
   color: HighlightText;
 }
 
 .addengine-icon {
@@ -226,18 +222,20 @@ menuitem[cmd="cmd_clearhistory"][disable
 }
 
 .search-panel-tree > .autocomplete-treebody::-moz-tree-image(fromhistory, selected) {
   list-style-image: url("chrome://browser/skin/search-history-icon.svg#search-history-icon-active");
 }
 
 .search-setting-button {
   -moz-appearance: none;
+  background-color: Menu;
   border: none;
-  border-top: 1px solid #ccc;
+  border-top: 1px solid rgba(0, 0, 0, 0.2);
   margin: 0;
   min-height: 32px;
 }
 
 .search-setting-button[selected] {
-  background-color: #d3d3d3;
+  background-color: Highlight;
+  color: HighlightText;
   border-top-color: #bdbebe;
 }
--- a/devtools/server/actors/styleeditor.js
+++ b/devtools/server/actors/styleeditor.js
@@ -450,17 +450,17 @@ var OldStyleSheetActor = protocol.ActorC
     if (!this.href) {
       // this is an inline <style> sheet
       let content = this.rawSheet.ownerNode.textContent;
       this.text = content;
       return promise.resolve(content);
     }
 
     let options = {
-      policy: Ci.nsIContentPolicy.TYPE_STYLESHEET,
+      policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
       window: this.window,
       charset: this._getCSSCharset()
     };
 
     return fetch(this.href, options).then(({ content }) => {
       this.text = content;
       return content;
     });
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -601,17 +601,17 @@ var StyleSheetActor = protocol.ActorClas
       // this is an inline <style> sheet
       let content = this.ownerNode.textContent;
       this.text = content;
       return promise.resolve(content);
     }
 
     let options = {
       loadFromCache: true,
-      policy: Ci.nsIContentPolicy.TYPE_STYLESHEET,
+      policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
       window: this.window,
       charset: this._getCSSCharset()
     };
 
     return fetch(this.href, options).then(({ content }) => {
       this.text = content;
       return content;
     });
@@ -687,17 +687,17 @@ var StyleSheetActor = protocol.ActorClas
         // no source map for this stylesheet
         deferred.resolve(null);
         return;
       };
 
       url = normalize(url, this.href);
       let options = {
         loadFromCache: false,
-        policy: Ci.nsIContentPolicy.TYPE_STYLESHEET,
+        policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
         window: this.window
       };
       let map = fetch(url, options)
         .then(({content}) => {
           let map = new SourceMapConsumer(content);
           this._setSourceMapRoot(map, url, this.href);
           this._sourceMap = promise.resolve(map);
 
@@ -1015,17 +1015,17 @@ var OriginalSourceActor = protocol.Actor
       return promise.resolve(this.text);
     }
     let content = this.sourceMap.sourceContentFor(this.url);
     if (content) {
       this.text = content;
       return promise.resolve(content);
     }
     let options = {
-      policy: Ci.nsIContentPolicy.TYPE_STYLESHEET,
+      policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
       window: this.window
     };
     return fetch(this.url, options).then(({content}) => {
       this.text = content;
       return content;
     });
   },
 
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -352,17 +352,17 @@ function saveToClipboard(context, reply)
                                .QueryInterface(Ci.nsILoadContext);
     const io = Cc["@mozilla.org/network/io-service;1"]
                   .getService(Ci.nsIIOService);
     const channel = io.newChannel2(reply.data, null, null,
                                    null,      // aLoadingNode
                                    Services.scriptSecurityManager.getSystemPrincipal(),
                                    null,      // aTriggeringPrincipal
                                    Ci.nsILoadInfo.SEC_NORMAL,
-                                   Ci.nsIContentPolicy.TYPE_IMAGE);
+                                   Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE);
     const input = channel.open();
     const imgTools = Cc["@mozilla.org/image/tools;1"]
                         .getService(Ci.imgITools);
 
     const container = {};
     imgTools.decodeImageData(input, channel.contentType, container);
 
     const wrapped = Cc["@mozilla.org/supports-interface-pointer;1"]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13917,18 +13917,65 @@ nsDocShell::ShouldPrepareForIntercept(ns
   *aShouldIntercept = swm->IsControlled(doc, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
+namespace {
+
+class FetchEventDispatcher final : public nsIFetchEventDispatcher
+{
+public:
+  FetchEventDispatcher(nsIInterceptedChannel* aChannel,
+                       nsIRunnable* aContinueRunnable)
+    : mChannel(aChannel)
+    , mContinueRunnable(aContinueRunnable)
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIFETCHEVENTDISPATCHER
+
+private:
+  ~FetchEventDispatcher()
+  {
+  }
+
+  nsCOMPtr<nsIInterceptedChannel> mChannel;
+  nsCOMPtr<nsIRunnable> mContinueRunnable;
+};
+
+NS_IMPL_ISUPPORTS(FetchEventDispatcher, nsIFetchEventDispatcher)
+
+NS_IMETHODIMP
+FetchEventDispatcher::Dispatch()
+{
+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+    return NS_OK;
+  }
+
+  ErrorResult error;
+  swm->DispatchPreparedFetchEvent(mChannel, mContinueRunnable, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+
+  return NS_OK;
+}
+
+}
+
+NS_IMETHODIMP
+nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel,
+                               nsIFetchEventDispatcher** aFetchDispatcher)
 {
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (!swm) {
     aChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
     return NS_OK;
   }
 
   bool isNavigation = false;
@@ -13944,21 +13991,27 @@ nsDocShell::ChannelIntercepted(nsIInterc
     }
   }
 
   bool isReload = mLoadType & LOAD_CMD_RELOAD;
 
   OriginAttributes attrs(GetAppId(), GetIsInBrowserElement());
 
   ErrorResult error;
-  swm->DispatchFetchEvent(attrs, doc, aChannel, isReload, error);
+  nsCOMPtr<nsIRunnable> runnable =
+    swm->PrepareFetchEvent(attrs, doc, aChannel, isReload, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
+  MOZ_ASSERT(runnable);
+  nsRefPtr<FetchEventDispatcher> dispatcher =
+    new FetchEventDispatcher(aChannel, runnable);
+  dispatcher.forget(aFetchDispatcher);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetPaymentRequestId(const nsAString& aPaymentRequestId)
 {
   mPaymentRequestId = aPaymentRequestId;
   return NS_OK;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1739,19 +1739,18 @@ Element::UnbindFromTree(bool aDeep, bool
         while (parent) {
           parent->ChangeEditableDescendantCount(editableDescendantChange);
           parent = parent->GetParent();
         }
       }
     }
 
     if (GetParent()) {
-      nsINode* p = mParent;
-      mParent = nullptr;
-      NS_RELEASE(p);
+      nsRefPtr<nsINode> p;
+      p.swap(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
 
   // Editable descendant count only counts descendants that
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -8,16 +8,17 @@
  * Implementation of the "@mozilla.org/layout/content-policy;1" contract.
  */
 
 #include "mozilla/Logging.h"
 
 #include "nsISupports.h"
 #include "nsXPCOM.h"
 #include "nsContentPolicyUtils.h"
+#include "mozilla/dom/nsCSPService.h"
 #include "nsContentPolicy.h"
 #include "nsIURI.h"
 #include "nsIDocShell.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindow.h"
 #include "nsIContent.h"
 #include "nsILoadContext.h"
@@ -118,19 +119,25 @@ nsContentPolicy::CheckPolicy(CPMethod   
     }
 
     nsContentPolicyType externalType =
         nsContentUtils::InternalContentPolicyTypeToExternal(contentType);
 
     nsContentPolicyType externalTypeOrScript =
         nsContentUtils::InternalContentPolicyTypeToExternalOrScript(contentType);
 
+    nsContentPolicyType externalTypeOrPreload =
+       nsContentUtils::InternalContentPolicyTypeToExternalOrPreload(contentType);
+
     nsCOMPtr<nsIContentPolicy> mixedContentBlocker =
         do_GetService(NS_MIXEDCONTENTBLOCKER_CONTRACTID);
 
+    nsCOMPtr<nsIContentPolicy> cspService =
+      do_GetService(CSPSERVICE_CONTRACTID);
+
     /* 
      * Enumerate mPolicies and ask each of them, taking the logical AND of
      * their permissions.
      */
     nsresult rv;
     nsCOMArray<nsIContentPolicy> entries;
     mPolicies.GetEntries(entries);
     int32_t count = entries.Count();
@@ -139,16 +146,25 @@ nsContentPolicy::CheckPolicy(CPMethod   
         // Send the internal content policy type to the mixed content blocker
         // which needs to know about TYPE_INTERNAL_WORKER,
         // TYPE_INTERNAL_SHARED_WORKER and TYPE_INTERNAL_SERVICE_WORKER.
         bool isMixedContentBlocker = mixedContentBlocker == entries[i];
         nsContentPolicyType type = externalType;
         if (isMixedContentBlocker) {
             type = externalTypeOrScript;
         }
+        // Send the internal content policy type for CSP which needs to
+        // know about preloads, in particular:
+        // * TYPE_INTERNAL_SCRIPT_PRELOAD
+        // * TYPE_INTERNAL_IMAGE_PRELOAD
+        // * TYPE_INTERNAL_STYLESHEET_PRELOAD
+        bool isCSP = cspService == entries[i];
+        if (isCSP) {
+          type = externalTypeOrPreload;
+        }
         rv = (entries[i]->*policyMethod)(type, contentLocation,
                                          requestingLocation, requestingContext,
                                          mimeType, extra, requestPrincipal,
                                          decision);
 
         if (NS_SUCCEEDED(rv) && NS_CP_REJECTED(*decision)) {
             /* policy says no, no point continuing to check */
             return NS_OK;
--- a/dom/base/nsContentPolicyUtils.h
+++ b/dom/base/nsContentPolicyUtils.h
@@ -87,51 +87,56 @@ NS_CP_ResponseName(int16_t response)
  *
  * @param contentType the content type code
  * @return the name of the given content type code
  */
 inline const char *
 NS_CP_ContentTypeName(uint32_t contentType)
 {
   switch (contentType) {
-    CASE_RETURN( TYPE_OTHER                   );
-    CASE_RETURN( TYPE_SCRIPT                  );
-    CASE_RETURN( TYPE_IMAGE                   );
-    CASE_RETURN( TYPE_STYLESHEET              );
-    CASE_RETURN( TYPE_OBJECT                  );
-    CASE_RETURN( TYPE_DOCUMENT                );
-    CASE_RETURN( TYPE_SUBDOCUMENT             );
-    CASE_RETURN( TYPE_REFRESH                 );
-    CASE_RETURN( TYPE_XBL                     );
-    CASE_RETURN( TYPE_PING                    );
-    CASE_RETURN( TYPE_XMLHTTPREQUEST          );
-    CASE_RETURN( TYPE_OBJECT_SUBREQUEST       );
-    CASE_RETURN( TYPE_DTD                     );
-    CASE_RETURN( TYPE_FONT                    );
-    CASE_RETURN( TYPE_MEDIA                   );
-    CASE_RETURN( TYPE_WEBSOCKET               );
-    CASE_RETURN( TYPE_CSP_REPORT              );
-    CASE_RETURN( TYPE_XSLT                    );
-    CASE_RETURN( TYPE_BEACON                  );
-    CASE_RETURN( TYPE_FETCH                   );
-    CASE_RETURN( TYPE_IMAGESET                );
-    CASE_RETURN( TYPE_WEB_MANIFEST            );
-    CASE_RETURN( TYPE_INTERNAL_SCRIPT         );
-    CASE_RETURN( TYPE_INTERNAL_WORKER         );
-    CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER  );
-    CASE_RETURN( TYPE_INTERNAL_EMBED          );
-    CASE_RETURN( TYPE_INTERNAL_OBJECT         );
-    CASE_RETURN( TYPE_INTERNAL_FRAME          );
-    CASE_RETURN( TYPE_INTERNAL_IFRAME         );
-    CASE_RETURN( TYPE_INTERNAL_AUDIO          );
-    CASE_RETURN( TYPE_INTERNAL_VIDEO          );
-    CASE_RETURN( TYPE_INTERNAL_TRACK          );
-    CASE_RETURN( TYPE_INTERNAL_XMLHTTPREQUEST );
-    CASE_RETURN( TYPE_INTERNAL_EVENTSOURCE    );
-    CASE_RETURN( TYPE_INTERNAL_SERVICE_WORKER );
+    CASE_RETURN( TYPE_OTHER                       );
+    CASE_RETURN( TYPE_SCRIPT                      );
+    CASE_RETURN( TYPE_IMAGE                       );
+    CASE_RETURN( TYPE_STYLESHEET                  );
+    CASE_RETURN( TYPE_OBJECT                      );
+    CASE_RETURN( TYPE_DOCUMENT                    );
+    CASE_RETURN( TYPE_SUBDOCUMENT                 );
+    CASE_RETURN( TYPE_REFRESH                     );
+    CASE_RETURN( TYPE_XBL                         );
+    CASE_RETURN( TYPE_PING                        );
+    CASE_RETURN( TYPE_XMLHTTPREQUEST              );
+    CASE_RETURN( TYPE_OBJECT_SUBREQUEST           );
+    CASE_RETURN( TYPE_DTD                         );
+    CASE_RETURN( TYPE_FONT                        );
+    CASE_RETURN( TYPE_MEDIA                       );
+    CASE_RETURN( TYPE_WEBSOCKET                   );
+    CASE_RETURN( TYPE_CSP_REPORT                  );
+    CASE_RETURN( TYPE_XSLT                        );
+    CASE_RETURN( TYPE_BEACON                      );
+    CASE_RETURN( TYPE_FETCH                       );
+    CASE_RETURN( TYPE_IMAGESET                    );
+    CASE_RETURN( TYPE_WEB_MANIFEST                );
+    CASE_RETURN( TYPE_INTERNAL_SCRIPT             );
+    CASE_RETURN( TYPE_INTERNAL_WORKER             );
+    CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER      );
+    CASE_RETURN( TYPE_INTERNAL_EMBED              );
+    CASE_RETURN( TYPE_INTERNAL_OBJECT             );
+    CASE_RETURN( TYPE_INTERNAL_FRAME              );
+    CASE_RETURN( TYPE_INTERNAL_IFRAME             );
+    CASE_RETURN( TYPE_INTERNAL_AUDIO              );
+    CASE_RETURN( TYPE_INTERNAL_VIDEO              );
+    CASE_RETURN( TYPE_INTERNAL_TRACK              );
+    CASE_RETURN( TYPE_INTERNAL_XMLHTTPREQUEST     );
+    CASE_RETURN( TYPE_INTERNAL_EVENTSOURCE        );
+    CASE_RETURN( TYPE_INTERNAL_SERVICE_WORKER     );
+    CASE_RETURN( TYPE_INTERNAL_SCRIPT_PRELOAD     );
+    CASE_RETURN( TYPE_INTERNAL_IMAGE              );
+    CASE_RETURN( TYPE_INTERNAL_IMAGE_PRELOAD      );
+    CASE_RETURN( TYPE_INTERNAL_STYLESHEET         );
+    CASE_RETURN( TYPE_INTERNAL_STYLESHEET_PRELOAD );
    default:
     return "<Unknown Type>";
   }
 }
 
 #undef CASE_RETURN
 
 /* Passes on parameters from its "caller"'s context. */
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7954,16 +7954,17 @@ nsContentUtils::GetWindowRoot(nsIDocumen
 }
 
 /* static */
 nsContentPolicyType
 nsContentUtils::InternalContentPolicyTypeToExternal(nsContentPolicyType aType)
 {
   switch (aType) {
   case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
+  case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
   case nsIContentPolicy::TYPE_INTERNAL_WORKER:
   case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
   case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
     return nsIContentPolicy::TYPE_SCRIPT;
 
   case nsIContentPolicy::TYPE_INTERNAL_EMBED:
   case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
     return nsIContentPolicy::TYPE_OBJECT;
@@ -7976,16 +7977,24 @@ nsContentUtils::InternalContentPolicyTyp
   case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
   case nsIContentPolicy::TYPE_INTERNAL_TRACK:
     return nsIContentPolicy::TYPE_MEDIA;
 
   case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
   case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
     return nsIContentPolicy::TYPE_XMLHTTPREQUEST;
 
+  case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
+  case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
+    return nsIContentPolicy::TYPE_IMAGE;
+
+  case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
+  case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
+    return nsIContentPolicy::TYPE_STYLESHEET;
+
   default:
     return aType;
   }
 }
 
 /* static */
 nsContentPolicyType
 nsContentUtils::InternalContentPolicyTypeToExternalOrScript(nsContentPolicyType aType)
@@ -7997,16 +8006,27 @@ nsContentUtils::InternalContentPolicyTyp
   case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
     return aType;
 
   default:
     return InternalContentPolicyTypeToExternal(aType);
   }
 }
 
+/* static */
+nsContentPolicyType
+nsContentUtils::InternalContentPolicyTypeToExternalOrPreload(nsContentPolicyType aType)
+{
+  if (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
+      aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
+      aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD) {
+    return aType;
+  }
+  return InternalContentPolicyTypeToExternal(aType);
+}
 
 nsresult
 nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                               nsIDocument* aDoc,
                                               nsIHttpChannel* aChannel)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aChannel);
@@ -8128,38 +8148,27 @@ nsContentUtils::InternalStorageAllowedFo
   // Check if we should only allow storage for the session, and record that fact
   if (sCookiesLifetimePolicy == nsICookieService::ACCEPT_SESSION) {
     // Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
     // so perform a std::min comparison to make sure we preserve ePrivateBrowsing
     // if it has been set.
     access = std::min(StorageAccess::eSessionScoped, access);
   }
 
-  // If the caller is chrome privileged, then it is allowed to access any
-  // storage it likes, no matter whether the storage for that window/principal
-  // would normally be permitted.
-  if (IsSystemPrincipal(SubjectPrincipal())) {
-    return access;
-  }
-
-  if (!SubjectPrincipal()->Subsumes(aPrincipal)) {
-    NS_WARNING("A principal is attempting to access storage for a principal "
-               "which it doesn't subsume!");
-    return StorageAccess::eDeny;
-  }
-
   // About URIs are allowed to access storage, even if they don't have chrome
   // privileges. If this is not desired, than the consumer will have to
   // implement their own restriction functionality.
   nsCOMPtr<nsIURI> uri;
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetURI(getter_AddRefs(uri))));
-  bool isAbout = false;
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)));
-  if (isAbout) {
-    return access;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  if (NS_SUCCEEDED(rv) && uri) {
+    bool isAbout = false;
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)));
+    if (isAbout) {
+      return access;
+    }
   }
 
   nsCOMPtr<nsIPermissionManager> permissionManager =
     services::GetPermissionManager();
   if (!permissionManager) {
     return StorageAccess::eDeny;
   }
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -619,33 +619,33 @@ public:
 
   /**
    * Method to do security and content policy checks on the image URI
    *
    * @param aURI uri of the image to be loaded
    * @param aContext the context the image is loaded in (eg an element)
    * @param aLoadingDocument the document we belong to
    * @param aLoadingPrincipal the principal doing the load
-   * @param [aContentPolicyType=nsIContentPolicy::TYPE_IMAGE] (Optional)
+   * @param [aContentPolicyType=nsIContentPolicy::TYPE_INTERNAL_IMAGE] (Optional)
    *        The CP content type to use
    * @param aImageBlockingStatus the nsIContentPolicy blocking status for this
    *        image.  This will be set even if a security check fails for the
    *        image, to some reasonable REJECT_* value.  This out param will only
    *        be set if it's non-null.
    * @return true if the load can proceed, or false if it is blocked.
    *         Note that aImageBlockingStatus, if set will always be an ACCEPT
    *         status if true is returned and always be a REJECT_* status if
    *         false is returned.
    */
   static bool CanLoadImage(nsIURI* aURI,
                            nsISupports* aContext,
                            nsIDocument* aLoadingDocument,
                            nsIPrincipal* aLoadingPrincipal,
                            int16_t* aImageBlockingStatus = nullptr,
-                           uint32_t aContentPolicyType = nsIContentPolicy::TYPE_IMAGE);
+                           uint32_t aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE);
 
   /**
    * Returns true if objects in aDocument shouldn't initiate image loads.
    */
   static bool DocumentInactiveForImageLoads(nsIDocument* aDocument);
 
   /**
    * Method to start an image load.  This does not do any security checks.
@@ -655,30 +655,30 @@ public:
    * @param aURI uri of the image to be loaded
    * @param aLoadingDocument the document we belong to
    * @param aLoadingPrincipal the principal doing the load
    * @param aReferrer the referrer URI
    * @param aReferrerPolicy the referrer-sending policy to use on channel
    *         creation
    * @param aObserver the observer for the image load
    * @param aLoadFlags the load flags to use.  See nsIRequest
-   * @param [aContentPolicyType=nsIContentPolicy::TYPE_IMAGE] (Optional)
+   * @param [aContentPolicyType=nsIContentPolicy::TYPE_INTERNAL_IMAGE] (Optional)
    *        The CP content type to use
    * @return the imgIRequest for the image load
    */
   static nsresult LoadImage(nsIURI* aURI,
                             nsIDocument* aLoadingDocument,
                             nsIPrincipal* aLoadingPrincipal,
                             nsIURI* aReferrer,
                             mozilla::net::ReferrerPolicy aReferrerPolicy,
                             imgINotificationObserver* aObserver,
                             int32_t aLoadFlags,
                             const nsAString& initiatorType,
                             imgRequestProxy** aRequest,
-                            uint32_t aContentPolicyType = nsIContentPolicy::TYPE_IMAGE);
+                            uint32_t aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE);
 
   /**
    * Obtain an image loader that respects the given document/channel's privacy status.
    * Null document/channel arguments return the public image loader.
    */
   static imgLoader* GetImgLoaderForDocument(nsIDocument* aDoc);
   static imgLoader* GetImgLoaderForChannel(nsIChannel* aChannel,
                                            nsIDocument* aContext);
@@ -961,16 +961,26 @@ public:
    *   * TYPE_INTERNAL_SERVICE_WORKER
    *
    *
    * Note: DO NOT call this function unless you know what you're doing!
    */
   static nsContentPolicyType InternalContentPolicyTypeToExternalOrScript(nsContentPolicyType aType);
 
   /**
+   * Map internal content policy types to external ones or preload types:
+   *   * TYPE_INTERNAL_SCRIPT_PRELOAD
+   *   * TYPE_INTERNAL_IMAGE_PRELOAD
+   *   * TYPE_INTERNAL_STYLESHEET_PRELOAD
+   *
+   * Note: DO NOT call this function unless you know what you're doing!
+   */
+  static nsContentPolicyType InternalContentPolicyTypeToExternalOrPreload(nsContentPolicyType aType);
+
+  /**
    * Quick helper to determine whether there are any mutation listeners
    * of a given type that apply to this content or any of its ancestors.
    * The method has the side effect to call document's MayDispatchMutationEvent
    * using aTargetForSubtreeModified as the parameter.
    *
    * @param aNode  The node to search for listeners
    * @param aType  The type of listener (NS_EVENT_BITS_MUTATION_*)
    * @param aTargetForSubtreeModified The node which is the target of the
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9734,17 +9734,18 @@ nsDocument::MaybePreLoadImage(nsIURI* ur
                               ReferrerPolicy aReferrerPolicy)
 {
   // Early exit if the img is already present in the img-cache
   // which indicates that the "real" load has already started and
   // that we shouldn't preload it.
   int16_t blockingStatus;
   if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
       !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
-                                    this, NodePrincipal(), &blockingStatus)) {
+                                    this, NodePrincipal(), &blockingStatus,
+                                    nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) {
     return;
   }
 
   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
   switch (Element::StringToCORSMode(aCrossOriginAttr)) {
   case CORS_NONE:
     // Nothing to do
     break;
@@ -9764,17 +9765,18 @@ nsDocument::MaybePreLoadImage(nsIURI* ur
     nsContentUtils::LoadImage(uri,
                               this,
                               NodePrincipal(),
                               mDocumentURI, // uri of document used as referrer
                               aReferrerPolicy,
                               nullptr,       // no observer
                               loadFlags,
                               NS_LITERAL_STRING("img"),
-                              getter_AddRefs(request));
+                              getter_AddRefs(request),
+                              nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD);
 
   // Pin image-reference to avoid evicting it from the img-cache before
   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
   // unlink
   if (NS_SUCCEEDED(rv)) {
     mPreloadingImages.Put(uri, request.forget());
   }
 }
@@ -9878,17 +9880,17 @@ nsDocument::PreloadStyle(nsIURI* uri, co
                          const nsAString& aCrossOriginAttr,
                          const ReferrerPolicy aReferrerPolicy,
                          const nsAString& aIntegrity)
 {
   // The CSSLoader will retain this object after we return.
   nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
 
   // Charset names are always ASCII.
-  CSSLoader()->LoadSheet(uri, NodePrincipal(),
+  CSSLoader()->LoadSheet(uri, true, NodePrincipal(),
                          NS_LossyConvertUTF16toASCII(charset),
                          obs,
                          Element::StringToCORSMode(aCrossOriginAttr),
                          aReferrerPolicy, aIntegrity);
 }
 
 nsresult
 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
--- a/dom/base/nsIContentPolicy.idl
+++ b/dom/base/nsIContentPolicy.idl
@@ -15,17 +15,17 @@ interface nsIPrincipal;
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(ce321216-c404-40a7-a711-d80454ec6b76)]
+[scriptable,uuid(caad4f1f-d047-46ac-ae9d-dc598e4fb91b)]
 interface nsIContentPolicy : nsIContentPolicyBase
 {
   /**
    * Should the resource at this location be loaded?
    * ShouldLoad will be called before loading the resource at aContentLocation
    * to determine whether to start the load at all.
    *
    * @param aContentType      the type of content being tested. This will be one
--- a/dom/base/nsIContentPolicyBase.idl
+++ b/dom/base/nsIContentPolicyBase.idl
@@ -19,17 +19,17 @@ typedef unsigned long nsContentPolicyTyp
  * Interface for content policy mechanism.  Implementations of this
  * interface can be used to control loading of various types of out-of-line
  * content, or processing of certain types of in-line content.
  *
  * WARNING: do not block the caller from shouldLoad or shouldProcess (e.g.,
  * by launching a dialog to prompt the user for something).
  */
 
-[scriptable,uuid(8527ae0d-0c43-4413-bc46-85c0bcb66876)]
+[scriptable,uuid(17418187-d86f-48dd-92d1-238838df0a4e)]
 interface nsIContentPolicyBase : nsISupports
 {
   /**
    * Indicates a unset or bogus policy type.
    */
   const nsContentPolicyType TYPE_INVALID = 0;
 
   /**
@@ -275,16 +275,57 @@ interface nsIContentPolicyBase : nsISupp
    * Indicates an internal constant for scripts loaded through a service
    * worker.
    *
    * This will be mapped to TYPE_SCRIPT before being passed to content policy
    * implementations.
    */
   const nsContentPolicyType TYPE_INTERNAL_SERVICE_WORKER = 35;
 
+  /**
+   * Indicates an internal constant for *preloaded* scripts
+   * loaded through script elements.
+   *
+   * This will be mapped to TYPE_SCRIPT before being passed
+   * to content policy implementations.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_SCRIPT_PRELOAD = 36;
+
+  /**
+   * Indicates an internal constant for normal images.
+   *
+   * This will be mapped to TYPE_IMAGE before being passed
+   * to content policy implementations.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_IMAGE = 37;
+
+  /**
+   * Indicates an internal constant for *preloaded* images.
+   *
+   * This will be mapped to TYPE_IMAGE before being passed
+   * to content policy implementations.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_IMAGE_PRELOAD = 38;
+
+  /**
+   * Indicates an internal constant for normal stylesheets.
+   *
+   * This will be mapped to TYPE_STYLESHEET before being passed
+   * to content policy implementations.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_STYLESHEET = 39;
+
+  /**
+   * Indicates an internal constant for *preloaded* stylesheets.
+   *
+   * This will be mapped to TYPE_STYLESHEET before being passed
+   * to content policy implementations.
+   */
+  const nsContentPolicyType TYPE_INTERNAL_STYLESHEET_PRELOAD = 40;
+
   /* When adding new content types, please update nsContentBlocker,
    * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
    * implementations, the static_assert in dom/cache/DBSchema.cpp,
    * and other things that are not listed here that are related to
    * nsIContentPolicy. */
 
   //////////////////////////////////////////////////////////////////////
 
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -584,17 +584,17 @@ nsContentPolicyType
 nsImageLoadingContent::PolicyTypeForLoad(ImageLoadType aImageLoadType)
 {
   if (aImageLoadType == eImageLoadType_Imageset) {
     return nsIContentPolicy::TYPE_IMAGESET;
   }
 
   MOZ_ASSERT(aImageLoadType == eImageLoadType_Normal,
              "Unknown ImageLoadType type in PolicyTypeForLoad");
-  return nsIContentPolicy::TYPE_IMAGE;
+  return nsIContentPolicy::TYPE_INTERNAL_IMAGE;
 }
 
 int32_t
 nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
                                       ErrorResult& aError)
 {
   if (aRequest == mCurrentRequest) {
     return CURRENT_REQUEST;
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1525,17 +1525,17 @@ nsObjectLoadingContent::CheckProcessPoli
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "Must be an instance of content");
 
   nsIDocument* doc = thisContent->OwnerDoc();
   
   int32_t objectType;
   switch (mType) {
     case eType_Image:
-      objectType = nsIContentPolicy::TYPE_IMAGE;
+      objectType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
       break;
     case eType_Document:
       objectType = nsIContentPolicy::TYPE_DOCUMENT;
       break;
     case eType_Plugin:
       objectType = GetContentPolicyType();
       break;
     default:
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * A class that handles loading and evaluation of <script> elements.
  */
 
 #include "nsScriptLoader.h"
 
+#include "prsystem.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "xpcpublic.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIContent.h"
 #include "nsJSUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/Element.h"
@@ -218,20 +219,25 @@ IsScriptEventHandler(nsIContent* aScript
 
   return false;
 }
 
 nsresult
 nsScriptLoader::CheckContentPolicy(nsIDocument* aDocument,
                                    nsISupports *aContext,
                                    nsIURI *aURI,
-                                   const nsAString &aType)
+                                   const nsAString &aType,
+                                   bool aIsPreLoad)
 {
+  nsContentPolicyType contentPolicyType = aIsPreLoad
+                                          ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD
+                                          : nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
+
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
+  nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
                                           aURI,
                                           aDocument->NodePrincipal(),
                                           aContext,
                                           NS_LossyConvertUTF16toASCII(aType),
                                           nullptr,    //extra
                                           &shouldLoad,
                                           nsContentUtils::GetContentPolicy(),
                                           nsContentUtils::GetSecurityManager());
@@ -244,42 +250,43 @@ nsScriptLoader::CheckContentPolicy(nsIDo
 
   return NS_OK;
 }
 
 nsresult
 nsScriptLoader::ShouldLoadScript(nsIDocument* aDocument,
                                  nsISupports* aContext,
                                  nsIURI* aURI,
-                                 const nsAString &aType)
+                                 const nsAString &aType,
+                                 bool aIsPreLoad)
 {
   // Check that the containing page is allowed to load this URI.
   nsresult rv = nsContentUtils::GetSecurityManager()->
     CheckLoadURIWithPrincipal(aDocument->NodePrincipal(), aURI,
                               nsIScriptSecurityManager::ALLOW_CHROME);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // After the security manager, the content-policy stuff gets a veto
-  rv = CheckContentPolicy(aDocument, aContext, aURI, aType);
+  rv = CheckContentPolicy(aDocument, aContext, aURI, aType, aIsPreLoad);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
                           bool aScriptFromHead)
 {
   nsISupports *context = aRequest->mElement.get()
                          ? static_cast<nsISupports *>(aRequest->mElement.get())
                          : static_cast<nsISupports *>(mDocument);
-  nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType);
+  nsresult rv = ShouldLoadScript(mDocument, context, aRequest->mURI, aType, aRequest->IsPreload());
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
 
   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(mDocument->MasterDocument()->GetWindow()));
 
@@ -291,22 +298,26 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
 
   nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));
 
   // If this document is sandboxed without 'allow-scripts', abort.
   if (mDocument->GetSandboxFlags() & SANDBOXED_SCRIPTS) {
     return NS_OK;
   }
 
+  nsContentPolicyType contentPolicyType = aRequest->IsPreload()
+                                          ? nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD
+                                          : nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
+
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      aRequest->mURI,
                      mDocument,
                      nsILoadInfo::SEC_NORMAL,
-                     nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
+                     contentPolicyType,
                      loadGroup,
                      prompter,
                      nsIRequest::LOAD_NORMAL |
                      nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIScriptElement *script = aRequest->mElement;
@@ -525,17 +536,17 @@ nsScriptLoader::ProcessScriptElement(nsI
 
       // Double-check that the charset the preload used is the same as
       // the charset we have now.
       nsAutoString elementCharset;
       aElement->GetScriptCharset(elementCharset);
       if (elementCharset.Equals(preloadCharset) &&
           ourCORSMode == request->mCORSMode &&
           ourRefPolicy == request->mReferrerPolicy) {
-        rv = CheckContentPolicy(mDocument, aElement, request->mURI, type);
+        rv = CheckContentPolicy(mDocument, aElement, request->mURI, type, false);
         NS_ENSURE_SUCCESS(rv, false);
       } else {
         // Drop the preload
         request = nullptr;
       }
     }
 
     if (!request) {
@@ -582,17 +593,17 @@ nsScriptLoader::ProcessScriptElement(nsI
     if (aElement->GetScriptAsync()) {
       request->mIsAsync = true;
       if (request->IsDoneLoading()) {
         mLoadedAsyncRequests.AppendElement(request);
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
 
         // KVKV TODO: Instead of processing immediately, try off-thread-parsing
-        // it and only schedule a ProcessRequest if that fails.
+        // it and only schedule a pending ProcessRequest if that fails.
         ProcessPendingRequestsAsync();
       } else {
         mLoadingAsyncRequests.AppendElement(request);
       }
       return false;
     }
     if (!aElement->GetParserCreated()) {
       // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
@@ -631,16 +642,17 @@ nsScriptLoader::ProcessScriptElement(nsI
       mXSLTRequests.AppendElement(request);
       if (request->IsDoneLoading()) {
         // The script is available already. Run it ASAP when the event
         // loop gets a chance to spin.
         ProcessPendingRequestsAsync();
       }
       return true;
     }
+
     if (request->IsDoneLoading() && ReadyToExecuteScripts()) {
       // The request has already been loaded and there are no pending style
       // sheets. If the script comes from the network stream, cheat for
       // performance reasons and avoid a trip through the event loop.
       if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
         return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
       }
       // Otherwise, we've got a document.written script, make a trip through
@@ -649,16 +661,17 @@ nsScriptLoader::ProcessScriptElement(nsI
       NS_ASSERTION(!mParserBlockingRequest,
           "There can be only one parser-blocking script at a time");
       NS_ASSERTION(mXSLTRequests.isEmpty(),
           "Parser-blocking scripts and XSLT scripts in the same doc!");
       mParserBlockingRequest = request;
       ProcessPendingRequestsAsync();
       return true;
     }
+
     // The script hasn't loaded yet or there's a style sheet blocking it.
     // The script will be run when it loads or the style sheet loads.
     NS_ASSERTION(!mParserBlockingRequest,
         "There can be only one parser-blocking script at a time");
     NS_ASSERTION(mXSLTRequests.isEmpty(),
         "Parser-blocking scripts and XSLT scripts in the same doc!");
     mParserBlockingRequest = request;
     return true;
@@ -747,16 +760,33 @@ public:
 
 } /* anonymous namespace */
 
 nsresult
 nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest)
 {
   MOZ_ASSERT(aRequest->mProgress == nsScriptLoadRequest::Progress_Compiling);
   aRequest->mProgress = nsScriptLoadRequest::Progress_DoneCompiling;
+  if (aRequest == mParserBlockingRequest) {
+    if (!ReadyToExecuteScripts()) {
+      // If not ready to execute scripts, schedule an async call to
+      // ProcessPendingRequests to handle it.
+      ProcessPendingRequestsAsync();
+      return NS_OK;
+    }
+
+    // Same logic as in top of ProcessPendingRequests.
+    mParserBlockingRequest = nullptr;
+    UnblockParser(aRequest);
+    ProcessRequest(aRequest);
+    mDocument->UnblockOnload(false);
+    ContinueParserAsync(aRequest);
+    return NS_OK;
+  }
+
   nsresult rv = ProcessRequest(aRequest);
   mDocument->UnblockOnload(false);
   return rv;
 }
 
 NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable()
 {
   if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) {
@@ -795,19 +825,20 @@ OffThreadScriptLoaderCallback(void *aTok
 {
   nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable =
     dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
   aRunnable->SetToken(aToken);
   NS_DispatchToMainThread(aRunnable);
 }
 
 nsresult
-nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
+nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
 {
-  if (!aRequest->mElement->GetScriptAsync() || aRequest->mIsInline) {
+  // Don't off-thread compile inline scripts.
+  if (aRequest->mIsInline) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
   if (!globalObject) {
     return NS_ERROR_FAILURE;
   }
 
@@ -838,27 +869,31 @@ nsScriptLoader::AttemptAsyncScriptParse(
   mDocument->BlockOnload();
   aRequest->mProgress = nsScriptLoadRequest::Progress_Compiling;
 
   unused << runnable.forget();
   return NS_OK;
 }
 
 nsresult
-nsScriptLoader::CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest)
+nsScriptLoader::CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest,
+                                                 bool* oCompiledOffThread)
 {
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Processing requests when running scripts is unsafe.");
   NS_ASSERTION(!aRequest->mOffThreadToken,
                "Candidate for off-thread compile is already parsed off-thread");
   NS_ASSERTION(!aRequest->InCompilingStage(),
                "Candidate for off-thread compile is already in compiling stage.");
 
-  nsresult rv = AttemptAsyncScriptParse(aRequest);
+  nsresult rv = AttemptAsyncScriptCompile(aRequest);
   if (rv != NS_ERROR_FAILURE) {
+    if (oCompiledOffThread && rv == NS_OK) {
+      *oCompiledOffThread = true;
+    }
     return rv;
   }
 
   return ProcessRequest(aRequest);
 }
 
 nsresult
 nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
@@ -1139,18 +1174,22 @@ void
 nsScriptLoader::ProcessPendingRequests()
 {
   nsRefPtr<nsScriptLoadRequest> request;
 
   if (mParserBlockingRequest &&
       mParserBlockingRequest->IsReadyToRun() &&
       ReadyToExecuteScripts()) {
     request.swap(mParserBlockingRequest);
+    bool offThreadCompiled = request->mProgress == nsScriptLoadRequest::Progress_DoneCompiling;
     UnblockParser(request);
     ProcessRequest(request);
+    if (offThreadCompiled) {
+      mDocument->UnblockOnload(false);
+    }
     ContinueParserAsync(request);
   }
 
   while (ReadyToExecuteScripts() && 
          !mXSLTRequests.isEmpty() &&
          mXSLTRequests.getFirst()->IsReadyToRun()) {
     request = mXSLTRequests.StealFirst();
     ProcessRequest(request);
@@ -1544,16 +1583,33 @@ nsScriptLoader::PrepareLoadedRequest(nsS
                mXSLTRequests.Contains(aRequest)  ||
                mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
                mParserBlockingRequest,
                "aRequest should be pending!");
 
   // Mark this as loaded
   aRequest->mProgress = nsScriptLoadRequest::Progress_DoneLoading;
 
+  // If this is currently blocking the parser, attempt to compile it off-main-thread.
+  if (aRequest == mParserBlockingRequest && (PR_GetNumberOfProcessors() > 1)) {
+    nsresult rv = AttemptAsyncScriptCompile(aRequest);
+    if (rv == NS_OK) {
+      NS_ASSERTION(aRequest->mProgress == nsScriptLoadRequest::Progress_Compiling,
+          "Request should be off-thread compiling now.");
+      return NS_OK;
+    }
+
+    // If off-thread compile errored, return the error.
+    if (rv != NS_ERROR_FAILURE) {
+      return rv;
+    }
+
+    // If off-thread compile was rejected, continue with regular processing.
+  }
+
   // And if it's async, move it to the loaded list.  aRequest->mIsAsync really
   // _should_ be in a list, but the consequences if it's not are bad enough we
   // want to avoid trying to move it if it's not.
   if (aRequest->mIsAsync) {
     MOZ_ASSERT(aRequest->isInList());
     if (aRequest->isInList()) {
       nsRefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
       mLoadedAsyncRequests.AppendElement(req);
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -349,17 +349,18 @@ public:
 
   /**
    * Check whether it's OK to load a script from aURI in
    * aDocument.
    */
   static nsresult ShouldLoadScript(nsIDocument* aDocument,
                                    nsISupports* aContext,
                                    nsIURI* aURI,
-                                   const nsAString &aType);
+                                   const nsAString &aType,
+                                   bool aIsPreLoad);
 
   /**
    * Starts deferring deferred scripts and puts them in the mDeferredRequests
    * queue instead.
    */
   void BeginDeferringScripts()
   {
     mDeferEnabled = true;
@@ -430,17 +431,18 @@ private:
 
 
   /**
    * Helper function to check the content policy for a given request.
    */
   static nsresult CheckContentPolicy(nsIDocument* aDocument,
                                      nsISupports *aContext,
                                      nsIURI *aURI,
-                                     const nsAString &aType);
+                                     const nsAString &aType,
+                                     bool aIsPreLoad);
 
   /**
    * Start a load for aRequest's URI.
    */
   nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
                      bool aScriptFromHead);
 
   /**
@@ -463,19 +465,20 @@ private:
   /**
    * Return whether just this loader is ready to execute scripts.
    */
   bool SelfReadyToExecuteScripts()
   {
     return mEnabled && !mBlockerCount;
   }
 
-  nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest);
+  nsresult AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest);
   nsresult ProcessRequest(nsScriptLoadRequest* aRequest);
-  nsresult CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest);
+  nsresult CompileOffThreadOrProcessRequest(nsScriptLoadRequest* aRequest,
+                                            bool* oCompiledOffThread=nullptr);
   void FireScriptAvailable(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   nsresult EvaluateScript(nsScriptLoadRequest* aRequest,
                           JS::SourceBufferHolder& aSrcBuf);
 
   already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -270,17 +270,22 @@ static_assert(nsIContentPolicy::TYPE_INV
               nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 &&
               nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 &&
               nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 &&
               nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
               nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 &&
               nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 &&
               nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 &&
               nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34 &&
-              nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35,
+              nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35 &&
+              nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD == 36 &&
+              nsIContentPolicy::TYPE_INTERNAL_IMAGE == 37 &&
+              nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD == 38 &&
+              nsIContentPolicy::TYPE_INTERNAL_STYLESHEET == 39 &&
+              nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD == 40,
               "nsContentPolicyType values are as expected");
 
 namespace {
 
 typedef int32_t EntryId;
 
 struct IdCount
 {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1403,16 +1403,17 @@ CanvasRenderingContext2D::EnsureTarget(R
 
 #if USE_SKIA_GPU
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
             AddDemotableContext(this);
+            mBufferProvider = new PersistentBufferProviderBasic(mTarget);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
             mode = RenderingMode::SoftwareBackendMode;
           }
         }
 #endif
       }
 
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -156,28 +156,18 @@ WebGLContext::IsExtensionSupported(WebGL
         if (gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_s3tc))
             return true;
 
         return gl->IsExtensionSupported(gl::GLContext::EXT_texture_compression_dxt1) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt3) &&
                gl->IsExtensionSupported(gl::GLContext::ANGLE_texture_compression_dxt5);
 
     case WebGLExtensionID::WEBGL_debug_renderer_info:
-        {
-            bool isEnabled = true;
+        return Preferences::GetBool("webgl.enable-debug-renderer-info", false);
 
-#ifdef RELEASE_BUILD
-            // Keep this disabled on Release and Beta for now. (see bug 1171228)
-            isEnabled = false;
-#endif
-            if (Preferences::GetBool("webgl.disable-debug-renderer-info", false))
-                isEnabled = false;
-
-            return isEnabled;
-        }
     case WebGLExtensionID::WEBGL_depth_texture:
         // WEBGL_depth_texture supports DEPTH_STENCIL textures
         if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
             return false;
 
         return gl->IsSupported(gl::GLFeature::depth_texture) ||
                gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
     case WebGLExtensionID::WEBGL_draw_buffers:
--- a/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
+++ b/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html
@@ -1,62 +1,54 @@
 <!DOCTYPE HTML>
 <html>
 <script>
 
-var Cu = parent.Components.utils;
-Cu.import("resource://gre/modules/AppConstants.jsm");
-// This gives us `AppConstants` in the global scope.
-// We need this because we only expose debug_renderer_info #ifndef MOZ_RELEASE_BUILD.
-// This should match AppConstants.RELEASE_BUILD.
 
 // This file has the portion of the test_webgl_renderer_info chrome mochitest
 // that has to run as non-chrome to check that this WebGL extension is not exposed to content
 
 // we can't call the chrome Mochitest ok() function ourselves from non-chrome code.
 // So we remote it to the chrome test.
 
 function ok(res, msg) {
   // Note we post to ourselves as posting to the chrome code doesn't seem to work here.
   // This works by having the chrome code put an event handler on our own window.
   window.postMessage({ subTestFinished: true, result: res, message: msg }, "*");
 }
 
 function messageListener(e) {
   // This is how the chrome test tells us to start running -- we have to wait for this
   // message to avoid running before it's set up its event handler.
-  if (e.data == "run") {
-    run();
+  if (e.data.run) {
+    var canBeUnprivileged = e.data.canBeUnprivileged;
+    run(canBeUnprivileged);
   }
 }
 
 window.addEventListener("message", messageListener, true);
 
-function run() {
+function run(canBeUnprivileged) {
   const UNMASKED_VENDOR_WEBGL = 0x9245;
   const UNMASKED_RENDERER_WEBGL = 0x9246;
 
-  var shouldHaveRendererInfo = false;
-  if (!AppConstants.RELEASE_BUILD)
-    shouldHaveRendererInfo = true;
-
   var canvas = document.createElement("canvas");
   var gl = canvas.getContext("experimental-webgl");
 
   ok(!gl.getError(), "getError on newly created WebGL context should return NO_ERROR");
 
   ok(!gl.getParameter(UNMASKED_VENDOR_WEBGL) && gl.getError() == gl.INVALID_ENUM,
       "Should not be able to query UNMASKED_VENDOR_WEBGL without having enabled the"
       + " WEBGL_debug_renderer_info extension");
   ok(!gl.getParameter(UNMASKED_RENDERER_WEBGL) && gl.getError() == gl.INVALID_ENUM,
       "Should not be able to query UNMASKED_RENDERER_WEBGL without having enabled the"
       + " WEBGL_debug_renderer_info extension");
 
   var exts = gl.getSupportedExtensions();
-  if (shouldHaveRendererInfo) {
+  if (canBeUnprivileged) {
     ok(exts.indexOf("WEBGL_debug_renderer_info") != -1,
        "WEBGL_debug_renderer_info should be listed by getSupportedExtensions in"
        + " non-chrome contexts on non-RELEASE_BUILDs");
 
     var ext = gl.getExtension("WEBGL_debug_renderer_info");
     ok(!!ext,
        "WEBGL_debug_renderer_info should be available through getExtension in non-chrome"
        + " contexts on non-RELEASE_BUILDs");
--- a/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
+++ b/dom/canvas/test/chrome/test_webgl_debug_renderer_info.html
@@ -12,16 +12,26 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 
 <pre id="test">
 <script>
 
 const UNMASKED_VENDOR_WEBGL = 0x9245;
 const UNMASKED_RENDERER_WEBGL = 0x9246;
 
+
+var Cu = parent.Components.utils;
+Cu.import("resource://gre/modules/AppConstants.jsm");
+// This gives us `AppConstants` in the global scope.
+// We need this because we only expose debug_renderer_info #ifndef MOZ_RELEASE_BUILD.
+// This should match AppConstants.RELEASE_BUILD.
+
+const canBeUnprivileged = !AppConstants.RELEASE_BUILD;
+
+
 function isNonEmptyString(s)
 {
   return s && (typeof s) == "string";
 }
 
 function messageListener(e) {
   if (e.data.allTestsFinished) {
     SimpleTest.finish();
@@ -62,27 +72,29 @@ function main()
 {
   SimpleTest.waitForExplicitFinish();
 
   checkChromeCase(document.createElement("canvas"));
 
   // Now run the non-chrome code to verify the security of this WebGL chrome-only extension.
 
   var iframe = document.createElement("iframe");
-  iframe.src = "chrome://mochitests/content/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
+  iframe.src = "http://mochi.test:8888/chrome/dom/canvas/test/chrome/nonchrome_webgl_debug_renderer_info.html";
 
   iframe.onload = function () {
 
     // test that chrome can get WEBGL_debug_renderer_info on a canvas on the iframe...
     // this is useful to check in itself, and is also useful so the subsequent non-chrome test
     // will also test that doing so doesn't confuse our chrome-only check.
     checkChromeCase(iframe.contentDocument.createElement("canvas"));
 
     iframe.contentWindow.addEventListener("message", messageListener, false);
-    iframe.contentWindow.postMessage("run", "*");
+    iframe.contentWindow.postMessage({run: true,
+                                      canBeUnprivileged: canBeUnprivileged},
+                                     "*");
   };
 
   document.body.appendChild(iframe);
 }
 
 window.onload = main;
 </script>
 </pre>
--- a/dom/canvas/test/webgl-mochitest/test_renderer_strings.html
+++ b/dom/canvas/test/webgl-mochitest/test_renderer_strings.html
@@ -7,74 +7,78 @@
 <body>
 <script>
 
 function AssertError(gl, expected, info) {
   var actual = gl.getError();
   while (gl.getError()) {}
 
   ok(actual == expected,
-     'For ' + info + ', expected 0x' + expected.toString(16) + ', got 0x'
+     'For ' + info + ', expected GL error 0x' + expected.toString(16) + ', got 0x'
       + actual.toString(16));
 }
 
 var gl;
 
 var RENDERER_OVERRIDE = 'overridden renderer';
 var VENDOR_OVERRIDE = 'overridden vendor';
 
 function TestExt() {
   var ext = gl.getExtension('WEBGL_debug_renderer_info');
-  ok(ext, 'Should have access to \'WEBGL_debug_renderer_info\'.');
+  ok(ext, 'When pref enabled: Should have access to \'WEBGL_debug_renderer_info\'.');
   AssertError(gl, 0, 'start of test');
 
   var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
   AssertError(gl, 0, 'UNMASKED_RENDERER_WEBGL');
   ok(renderer,
-     'UNMASKED_RENDERER_WEBGL value should not be empty, was \'' + renderer + '\'');
+     'When pref enabled: UNMASKED_RENDERER_WEBGL value should not be empty, was \''
+     + renderer + '\'');
 
   var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
   AssertError(gl, 0, 'UNMASKED_VENDOR_WEBGL');
-  ok(vendor, 'UNMASKED_VENDOR_WEBGL value should not be empty, was \'' + vendor + '\'');
+  ok(vendor,
+     'When pref enabled: UNMASKED_VENDOR_WEBGL value should not be empty, was \''
+     + vendor + '\'');
 
   var prefArrArr = [
     ['webgl.renderer-string-override', RENDERER_OVERRIDE],
     ['webgl.vendor-string-override', VENDOR_OVERRIDE],
   ];
   var prefEnv = {'set': prefArrArr};
   SpecialPowers.pushPrefEnv(prefEnv, TestOverrides);
 }
 
 function TestOverrides() {
   var ext = gl.getExtension('WEBGL_debug_renderer_info');
-  ok(ext, 'Should have access to \'WEBGL_debug_renderer_info\'.');
+  ok(ext, 'When overrides set: Should have access to \'WEBGL_debug_renderer_info\'.');
   AssertError(gl, 0, 'start of test');
 
   var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
   AssertError(gl, 0, 'UNMASKED_RENDERER_WEBGL');
   ok(renderer == RENDERER_OVERRIDE,
-     'UNMASKED_RENDERER_WEBGL value should be \'' + RENDERER_OVERRIDE + '\', was \''
-     + renderer + '\'');
+     'When overrides set: UNMASKED_RENDERER_WEBGL value should be \'' + RENDERER_OVERRIDE
+     + '\', was \'' + renderer + '\'');
 
   var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
   AssertError(gl, 0, 'UNMASKED_VENDOR_WEBGL');
   ok(vendor == VENDOR_OVERRIDE,
-     'UNMASKED_VENDOR_WEBGL value should be \'' + VENDOR_OVERRIDE + '\', was \'' + vendor
-     + '\'');
+     'When overrides set: UNMASKED_VENDOR_WEBGL value should be \'' + VENDOR_OVERRIDE
+     + '\', was \'' + vendor + '\'');
 
   var prefArrArr = [
-    ['webgl.disable-debug-renderer-info', true],
+    ['webgl.enable-debug-renderer-info', false],
   ];
   var prefEnv = {'set': prefArrArr};
   SpecialPowers.pushPrefEnv(prefEnv, TestDisable);
 }
 
 function TestDisable() {
   var ext = gl.getExtension('WEBGL_debug_renderer_info');
-  ok(!ext, 'Should not have access to \'WEBGL_debug_renderer_info\'.');
+  ok(!ext,
+     'When pref disabled: Should not have access to \'WEBGL_debug_renderer_info\'.');
 
   ok(true, 'Test complete.');
   SimpleTest.finish();
 }
 
 (function() {
   var canvas = document.createElement('canvas');
   gl = canvas.getContext('experimental-webgl');
@@ -82,17 +86,17 @@ function TestDisable() {
     todo(gl, 'Get WebGL working here first.');
     ok(true, 'Test complete.');
     return;
   }
 
   SimpleTest.waitForExplicitFinish();
 
   var prefArrArr = [
-    ['webgl.disable-debug-renderer-info', false],
+    ['webgl.enable-debug-renderer-info', true],
   ];
   var prefEnv = {'set': prefArrArr};
   SpecialPowers.pushPrefEnv(prefEnv, TestExt);
 })();
 
 </script>
 </body>
 </html>
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -111,29 +111,32 @@ RequestContext
 InternalRequest::MapContentPolicyTypeToRequestContext(nsContentPolicyType aContentPolicyType)
 {
   RequestContext context = RequestContext::Internal;
   switch (aContentPolicyType) {
   case nsIContentPolicy::TYPE_OTHER:
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
+  case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
   case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
     context = RequestContext::Script;
     break;
   case nsIContentPolicy::TYPE_INTERNAL_WORKER:
     context = RequestContext::Worker;
     break;
   case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
     context = RequestContext::Sharedworker;
     break;
-  case nsIContentPolicy::TYPE_IMAGE:
+  case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
+  case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
     context = RequestContext::Image;
     break;
-  case nsIContentPolicy::TYPE_STYLESHEET:
+  case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
+  case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
     context = RequestContext::Style;
     break;
   case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
     context = RequestContext::Object;
     break;
   case nsIContentPolicy::TYPE_INTERNAL_EMBED:
     context = RequestContext::Embed;
     break;
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -38,30 +38,30 @@ namespace dom {
  * eventsource       |
  * favicon           |
  * fetch             | TYPE_FETCH
  * font              | TYPE_FONT
  * form              |
  * frame             | TYPE_INTERNAL_FRAME
  * hyperlink         |
  * iframe            | TYPE_INTERNAL_IFRAME
- * image             | TYPE_IMAGE
+ * image             | TYPE_INTERNAL_IMAGE, TYPE_INTERNAL_IMAGE_PRELOAD
  * imageset          | TYPE_IMAGESET
  * import            | Not supported by Gecko
  * internal          | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER
  * location          |
  * manifest          | TYPE_WEB_MANIFEST
  * object            | TYPE_INTERNAL_OBJECT
  * ping              | TYPE_PING
  * plugin            | TYPE_OBJECT_SUBREQUEST
  * prefetch          |
- * script            | TYPE_INTERNAL_SCRIPT
+ * script            | TYPE_INTERNAL_SCRIPT, TYPE_INTERNAL_SCRIPT_PRELOAD
  * sharedworker      | TYPE_INTERNAL_SHARED_WORKER
  * subresource       | Not supported by Gecko
- * style             | TYPE_STYLESHEET
+ * style             | TYPE_INTERNAL_STYLESHEET, TYPE_INTERNAL_STYLESHEET_PRELOAD
  * track             | TYPE_INTERNAL_TRACK
  * video             | TYPE_INTERNAL_VIDEO
  * worker            | TYPE_INTERNAL_WORKER
  * xmlhttprequest    | TYPE_INTERNAL_XMLHTTPREQUEST
  * eventsource       | TYPE_INTERNAL_EVENTSOURCE
  * xslt              | TYPE_XSLT
  *
  * TODO: Figure out if TYPE_REFRESH maps to anything useful
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -22,17 +22,16 @@
 
 #undef LOG
 #define LOG(args...) FM_LOG("FMRadio", args)
 
 // The pref indicates if the device has an internal antenna.
 // If the pref is true, the antanna will be always available.
 #define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
 
-using namespace mozilla::hal;
 using mozilla::Preferences;
 
 BEGIN_FMRADIO_NAMESPACE
 
 class FMRadioRequest final : public FMRadioReplyRunnable
                            , public DOMRequest
 {
 public:
@@ -101,17 +100,17 @@ protected:
 private:
   FMRadioRequestArgs::Type mType;
   nsWeakPtr mFMRadio;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
 
 FMRadio::FMRadio()
-  : mHeadphoneState(SWITCH_STATE_OFF)
+  : mHeadphoneState(hal::SWITCH_STATE_OFF)
   , mRdsGroupMask(0)
   , mAudioChannelAgentEnabled(false)
   , mHasInternalAntenna(false)
   , mIsShutdown(false)
 {
   LOG("FMRadio is initialized.");
 }
 
@@ -126,18 +125,18 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
 
   IFMRadioService::Singleton()->AddObserver(this);
 
   mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
                                              /* default = */ false);
   if (mHasInternalAntenna) {
     LOG("We have an internal antenna.");
   } else {
-    mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
-    RegisterSwitchObserver(SWITCH_HEADPHONES, this);
+    mHeadphoneState = hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES);
+    hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
   }
 
   nsCOMPtr<nsIAudioChannelAgent> audioChannelAgent =
     do_CreateInstance("@mozilla.org/audiochannelagent;1");
   NS_ENSURE_TRUE_VOID(audioChannelAgent);
 
   audioChannelAgent->InitWithWeakCallback(
     GetOwner(),
@@ -150,30 +149,30 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
 }
 
 void
 FMRadio::Shutdown()
 {
   IFMRadioService::Singleton()->RemoveObserver(this);
 
   if (!mHasInternalAntenna) {
-    UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
+    hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
   }
 
   mIsShutdown = true;
 }
 
 JSObject*
 FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return FMRadioBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-FMRadio::Notify(const SwitchEvent& aEvent)
+FMRadio::Notify(const hal::SwitchEvent& aEvent)
 {
   MOZ_ASSERT(!mHasInternalAntenna);
 
   if (mHeadphoneState != aEvent.status()) {
     mHeadphoneState = aEvent.status();
 
     DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
   }
@@ -236,18 +235,18 @@ bool
 FMRadio::RdsEnabled()
 {
   return IFMRadioService::Singleton()->IsRDSEnabled();
 }
 
 bool
 FMRadio::AntennaAvailable() const
 {
-  return mHasInternalAntenna ? true : (mHeadphoneState != SWITCH_STATE_OFF) &&
-    (mHeadphoneState != SWITCH_STATE_UNKNOWN);
+  return mHasInternalAntenna ? true : (mHeadphoneState != hal::SWITCH_STATE_OFF) &&
+    (mHeadphoneState != hal::SWITCH_STATE_UNKNOWN);
 }
 
 Nullable<double>
 FMRadio::GetFrequency() const
 {
   return Enabled() ?
     Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) :
     Nullable<double>();
@@ -382,31 +381,31 @@ already_AddRefed<DOMRequest>
 FMRadio::SeekUp()
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return nullptr;
   }
 
   nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r);
+  IFMRadioService::Singleton()->Seek(hal::FM_RADIO_SEEK_DIRECTION_UP, r);
 
   return r.forget();
 }
 
 already_AddRefed<DOMRequest>
 FMRadio::SeekDown()
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return nullptr;
   }
 
   nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r);
+  IFMRadioService::Singleton()->Seek(hal::FM_RADIO_SEEK_DIRECTION_DOWN, r);
 
   return r.forget();
 }
 
 already_AddRefed<DOMRequest>
 FMRadio::CancelSeek()
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
--- a/dom/fmradio/FMRadioService.cpp
+++ b/dom/fmradio/FMRadioService.cpp
@@ -28,17 +28,16 @@
 #define BAND_76000_108000_kHz 2
 #define BAND_76000_90000_kHz  3
 
 #define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
 #define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
 
 #define DOM_PARSED_RDS_GROUPS ((0x2 << 30) | (0x3 << 4) | (0x3 << 0))
 
-using namespace mozilla::hal;
 using mozilla::Preferences;
 
 BEGIN_FMRADIO_NAMESPACE
 
 // static
 IFMRadioService*
 IFMRadioService::Singleton()
 {
@@ -73,19 +72,19 @@ FMRadioService::FMRadioService()
   , mRadiotextSet(false)
 {
   memset(mPSName, 0, sizeof(mPSName));
   memset(mRadiotext, 0, sizeof(mRadiotext));
   memset(mTempPSName, 0, sizeof(mTempPSName));
   memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
 
   // Read power state and frequency from Hal.
-  mEnabled = IsFMRadioOn();
+  mEnabled = hal::IsFMRadioOn();
   if (mEnabled) {
-    mPendingFrequencyInKHz = GetFMRadioFrequency();
+    mPendingFrequencyInKHz = hal::GetFMRadioFrequency();
     SetState(Enabled);
   }
 
   switch (Preferences::GetInt("dom.fmradio.band", BAND_87500_108000_kHz)) {
     case BAND_76000_90000_kHz:
       mUpperBoundInKHz = 90000;
       mLowerBoundInKHz = 76000;
       break;
@@ -128,100 +127,79 @@ FMRadioService::FMRadioService()
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 
   if (obs && NS_FAILED(obs->AddObserver(this,
                                         MOZSETTINGS_CHANGED_ID,
                                         /* useWeak */ false))) {
     NS_WARNING("Failed to add settings change observer!");
   }
 
-  RegisterFMRadioObserver(this);
-  RegisterFMRadioRDSObserver(this);
+  hal::RegisterFMRadioObserver(this);
+  hal::RegisterFMRadioRDSObserver(this);
 }
 
 FMRadioService::~FMRadioService()
 {
-  UnregisterFMRadioRDSObserver(this);
-  UnregisterFMRadioObserver(this);
+  hal::UnregisterFMRadioRDSObserver(this);
+  hal::UnregisterFMRadioObserver(this);
 }
 
-class EnableRunnable final : public nsRunnable
+void
+FMRadioService::EnableFMRadio()
 {
-public:
-  EnableRunnable(uint32_t aUpperLimit, uint32_t aLowerLimit, uint32_t aSpaceType, uint32_t aPreemphasis)
-    : mUpperLimit(aUpperLimit)
-    , mLowerLimit(aLowerLimit)
-    , mSpaceType(aSpaceType)
-    , mPreemphasis(aPreemphasis)
-  {
-  }
-
-  NS_IMETHOD Run()
-  {
-    FMRadioSettings info;
-    info.upperLimit() = mUpperLimit;
-    info.lowerLimit() = mLowerLimit;
-    info.spaceType() = mSpaceType;
-    info.preEmphasis() = mPreemphasis;
+  hal::FMRadioSettings info;
+  info.upperLimit() = mUpperBoundInKHz;
+  info.lowerLimit() = mLowerBoundInKHz;
+  info.spaceType() = mChannelWidthInKHz;
+  info.preEmphasis() = mPreemphasis;
 
-    EnableFMRadio(info);
-
-    FMRadioService* fmRadioService = FMRadioService::Singleton();
-    if (!fmRadioService->mTuneThread) {
-      // SeekRunnable and SetFrequencyRunnable run on this thread. These
-      // call ioctls that can stall the main thread, so we run them here.
-      fmRadioService->mTuneThread = new LazyIdleThread(
-        TUNE_THREAD_TIMEOUT_MS, NS_LITERAL_CSTRING("FM Tuning"));
-    }
+  hal::EnableFMRadio(info);
 
-    return NS_OK;
+  if (!mTuneThread) {
+    // hal::FMRadioSeek and hal::SetFMRadioFrequency run on this thread. These
+    // call ioctls that can stall the main thread, so we run them here.
+    mTuneThread = new LazyIdleThread(
+      TUNE_THREAD_TIMEOUT_MS, NS_LITERAL_CSTRING("FM Tuning"));
   }
-
-private:
-  uint32_t mUpperLimit;
-  uint32_t mLowerLimit;
-  uint32_t mSpaceType;
-  uint32_t mPreemphasis;
-};
+}
 
 /**
  * Read the airplane-mode setting, if the airplane-mode is not enabled, we
  * enable the FM radio.
  */
 class ReadAirplaneModeSettingTask final : public nsISettingsServiceCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   ReadAirplaneModeSettingTask(nsRefPtr<FMRadioReplyRunnable> aPendingRequest)
     : mPendingRequest(aPendingRequest) { }
 
   NS_IMETHOD
   Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   {
-    FMRadioService* fmRadioService = FMRadioService::Singleton();
+    nsRefPtr<FMRadioService> fmRadioService = FMRadioService::Singleton();
     MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
-
+    
     fmRadioService->mHasReadAirplaneModeSetting = true;
 
     if (!aResult.isBoolean()) {
       // Failed to read the setting value, set the state back to Disabled.
       fmRadioService->TransitionState(
         ErrorResponse(NS_LITERAL_STRING("Unexpected error")), Disabled);
       return NS_OK;
     }
 
     fmRadioService->mAirplaneModeEnabled = aResult.toBoolean();
     if (!fmRadioService->mAirplaneModeEnabled) {
-      EnableRunnable* runnable =
-        new EnableRunnable(fmRadioService->mUpperBoundInKHz,
-                           fmRadioService->mLowerBoundInKHz,
-                           fmRadioService->mChannelWidthInKHz,
-                           fmRadioService->mPreemphasis);
-      NS_DispatchToMainThread(runnable);
+      NS_DispatchToMainThread(NS_NewRunnableFunction(
+        [fmRadioService] () -> void {
+          fmRadioService->EnableFMRadio();
+        }
+      ));
     } else {
       // Airplane mode is enabled, set the state back to Disabled.
       fmRadioService->TransitionState(ErrorResponse(
         NS_LITERAL_STRING("Airplane mode currently enabled")), Disabled);
     }
 
     return NS_OK;
   }
@@ -242,90 +220,39 @@ protected:
   ~ReadAirplaneModeSettingTask() {}
 
 private:
   nsRefPtr<FMRadioReplyRunnable> mPendingRequest;
 };
 
 NS_IMPL_ISUPPORTS(ReadAirplaneModeSettingTask, nsISettingsServiceCallback)
 
-class DisableRunnable final : public nsRunnable
+void
+FMRadioService::DisableFMRadio()
 {
-public:
-  DisableRunnable() { }
-
-  NS_IMETHOD Run()
-  {
-    FMRadioService* fmRadioService = FMRadioService::Singleton();
-    if (fmRadioService->mTuneThread) {
-      fmRadioService->mTuneThread->Shutdown();
-      fmRadioService->mTuneThread = nullptr;
-    }
-    // Fix Bug 796733. DisableFMRadio should be called before
-    // SetFmRadioAudioEnabled to prevent the annoying beep sound.
-    DisableFMRadio();
-    fmRadioService->EnableAudio(false);
-
-    return NS_OK;
+  if (mTuneThread) {
+    mTuneThread->Shutdown();
+    mTuneThread = nullptr;
   }
-};
-
-class SetFrequencyRunnable final : public nsRunnable
-{
-public:
-  SetFrequencyRunnable(int32_t aFrequency)
-    : mFrequency(aFrequency) { }
-
-  NS_IMETHOD Run()
-  {
-    SetFMRadioFrequency(mFrequency);
-    return NS_OK;
-  }
-
-private:
-  int32_t mFrequency;
+  // Fix Bug 796733. DisableFMRadio should be called before
+  // SetFmRadioAudioEnabled to prevent the annoying beep sound.
+  hal::DisableFMRadio();
+  EnableAudio(false);
 };
 
-class SeekRunnable final : public nsRunnable
+void
+FMRadioService::DispatchFMRadioEventToMainThread(enum FMRadioEventType aType)
 {
-public:
-  SeekRunnable(FMRadioSeekDirection aDirection) : mDirection(aDirection) { }
-
-  NS_IMETHOD Run()
-  {
-    switch (mDirection) {
-      case FM_RADIO_SEEK_DIRECTION_UP:
-      case FM_RADIO_SEEK_DIRECTION_DOWN:
-        FMRadioSeek(mDirection);
-        break;
-      default:
-        MOZ_CRASH();
+  nsRefPtr<FMRadioService> self = this;
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+    [self, aType] () -> void {
+      self->NotifyFMRadioEvent(aType);
     }
-
-    return NS_OK;
-  }
-
-private:
-  FMRadioSeekDirection mDirection;
-};
-
-class NotifyRunnable final : public nsRunnable
-{
-public:
-  NotifyRunnable(FMRadioEventType aType) : mType(aType) { }
-
-  NS_IMETHOD Run()
-  {
-    FMRadioService::Singleton()->NotifyFMRadioEvent(mType);
-    return NS_OK;
-  }
-
-private:
-  FMRadioEventType mType;
-};
+  ));
+}
 
 void
 FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
                                 FMRadioState aState)
 {
   if (mPendingRequest) {
     mPendingRequest->SetReply(aResponse);
     NS_DispatchToMainThread(mPendingRequest);
@@ -352,17 +279,17 @@ void
 FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   mObserverList.RemoveObserver(aObserver);
 
   if (mObserverList.Length() == 0)
   {
     // Turning off the FM radio HW because observer list is empty.
-    if (IsFMRadioOn()) {
+    if (hal::IsFMRadioOn()) {
       DoDisable();
     }
   }
 }
 
 void
 FMRadioService::EnableAudio(bool aAudioEnabled)
 {
@@ -411,32 +338,32 @@ FMRadioService::RoundFrequency(double aF
 
   return mLowerBoundInKHz + roundedPart;
 }
 
 bool
 FMRadioService::IsEnabled() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return IsFMRadioOn();
+  return hal::IsFMRadioOn();
 }
 
 bool
 FMRadioService::IsRDSEnabled() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   return mRDSEnabled;
 }
 
 double
 FMRadioService::GetFrequency() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   if (IsEnabled()) {
-    int32_t frequencyInKHz = GetFMRadioFrequency();
+    int32_t frequencyInKHz = hal::GetFMRadioFrequency();
     return frequencyInKHz / 1000.0;
   }
 
   return 0;
 }
 
 double
 FMRadioService::GetFrequencyUpperBound() const
@@ -578,20 +505,22 @@ FMRadioService::Enable(double aFrequency
     if (NS_FAILED(rv)) {
       TransitionState(ErrorResponse(
         NS_LITERAL_STRING("Can't get settings lock")), Disabled);
     }
 
     return;
   }
 
-  NS_DispatchToMainThread(new EnableRunnable(mUpperBoundInKHz,
-                                             mLowerBoundInKHz,
-                                             mChannelWidthInKHz,
-                                             mPreemphasis));
+  nsRefPtr<FMRadioService> self = this;
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+    [self] () -> void {
+      self->EnableFMRadio();
+    }
+  ));
 }
 
 void
 FMRadioService::Disable(FMRadioReplyRunnable* aReplyRunnable)
 {
   // When airplane-mode is enabled, we will call this function from
   // FMRadioService::Observe without passing a FMRadioReplyRunnable,
   // so we have to check if |aReplyRunnable| is null before we dispatch it.
@@ -660,18 +589,23 @@ void
 FMRadioService::DoDisable()
 {
   // To make such codes work:
   //    navigator.mozFMRadio.disable();
   //    navigator.mozFMRadio.ondisabled = function() {
   //      console.log("We will catch disabled event ");
   //    };
   // we need to call hal::DisableFMRadio() asynchronously. Same reason for
-  // EnableRunnable and SetFrequencyRunnable.
-  NS_DispatchToMainThread(new DisableRunnable());
+  // EnableFMRadio and hal::SetFMRadioFrequency.
+  nsRefPtr<FMRadioService> self = this;
+  NS_DispatchToMainThread(NS_NewRunnableFunction(
+    [self] () -> void {
+      self->DisableFMRadio();
+    }
+  ));
 }
 
 void
 FMRadioService::SetFrequency(double aFrequencyInMHz,
                              FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aReplyRunnable);
@@ -688,42 +622,46 @@ FMRadioService::SetFrequency(double aFre
       NS_DispatchToMainThread(aReplyRunnable);
       return;
     case Disabling:
       aReplyRunnable->SetReply(
         ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
       NS_DispatchToMainThread(aReplyRunnable);
       return;
     case Seeking:
-      CancelFMRadioSeek();
+      hal::CancelFMRadioSeek();
       TransitionState(ErrorResponse(
         NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
       break;
     case Enabled:
       break;
   }
 
   int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
 
   if (!roundedFrequency) {
     aReplyRunnable->SetReply(ErrorResponse(
       NS_LITERAL_STRING("Frequency is out of range")));
     NS_DispatchToMainThread(aReplyRunnable);
     return;
   }
 
-  mTuneThread->Dispatch(new SetFrequencyRunnable(roundedFrequency),
-                        nsIThread::DISPATCH_NORMAL);
+  mTuneThread->Dispatch(
+    NS_NewRunnableFunction(
+      [roundedFrequency] () -> void {
+        hal::SetFMRadioFrequency(roundedFrequency);
+      }
+    ), nsIThread::DISPATCH_NORMAL);
 
   aReplyRunnable->SetReply(SuccessResponse());
   NS_DispatchToMainThread(aReplyRunnable);
 }
 
 void
-FMRadioService::Seek(FMRadioSeekDirection aDirection,
+FMRadioService::Seek(hal::FMRadioSeekDirection aDirection,
                      FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aReplyRunnable);
 
   switch (mState) {
     case Enabling:
       aReplyRunnable->SetReply(
@@ -747,17 +685,29 @@ FMRadioService::Seek(FMRadioSeekDirectio
       return;
     case Enabled:
       break;
   }
 
   SetState(Seeking);
   mPendingRequest = aReplyRunnable;
 
-  mTuneThread->Dispatch(new SeekRunnable(aDirection), nsIThread::DISPATCH_NORMAL);
+  mTuneThread->Dispatch(
+    NS_NewRunnableFunction(
+      [aDirection] () -> void {
+        switch (aDirection) {
+          case hal::FM_RADIO_SEEK_DIRECTION_UP:
+          case hal::FM_RADIO_SEEK_DIRECTION_DOWN:
+            hal::FMRadioSeek(aDirection);
+            break;
+          default:
+            MOZ_CRASH();
+        }
+      }
+    ), nsIThread::DISPATCH_NORMAL);
 }
 
 void
 FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aReplyRunnable);
 
@@ -765,73 +715,75 @@ FMRadioService::CancelSeek(FMRadioReplyR
   if (mState != Seeking) {
     aReplyRunnable->SetReply(
       ErrorResponse(NS_LITERAL_STRING("FM radio currently not seeking")));
     NS_DispatchToMainThread(aReplyRunnable);
     return;
   }
 
   // Cancel the seek immediately to prevent it from completing.
-  CancelFMRadioSeek();
+  hal::CancelFMRadioSeek();
 
   TransitionState(
     ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
 
   aReplyRunnable->SetReply(SuccessResponse());
   NS_DispatchToMainThread(aReplyRunnable);
 }
 
 void
 FMRadioService::SetRDSGroupMask(uint32_t aRDSGroupMask)
 {
   mRDSGroupMask = aRDSGroupMask;
-  if (IsFMRadioOn() && mRDSEnabled) {
+  if (hal::IsFMRadioOn() && mRDSEnabled) {
     DebugOnly<bool> enabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
     MOZ_ASSERT(enabled);
   }
 }
 
 void
 FMRadioService::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aReplyRunnable);
 
-  if (IsFMRadioOn()) {
+  if (hal::IsFMRadioOn()) {
     if (!hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS)) {
       aReplyRunnable->SetReply(
         ErrorResponse(NS_LITERAL_STRING("Could not enable RDS")));
       NS_DispatchToMainThread(aReplyRunnable);
       return;
     }
   }
 
   mRDSEnabled = true;
 
   aReplyRunnable->SetReply(SuccessResponse());
   NS_DispatchToMainThread(aReplyRunnable);
-  NS_DispatchToMainThread(new NotifyRunnable(RDSEnabledChanged));
+
+  DispatchFMRadioEventToMainThread(RDSEnabledChanged);
 }
 
 void
 FMRadioService::DisableRDS(FMRadioReplyRunnable* aReplyRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
   MOZ_ASSERT(aReplyRunnable);
 
-  if (IsFMRadioOn()) {
+  if (hal::IsFMRadioOn()) {
     hal::DisableRDS();
   }
 
   aReplyRunnable->SetReply(SuccessResponse());
   NS_DispatchToMainThread(aReplyRunnable);
 
   if (mRDSEnabled) {
     mRDSEnabled = false;
-    NS_DispatchToMainThread(new NotifyRunnable(RDSEnabledChanged));
+
+    DispatchFMRadioEventToMainThread(RDSEnabledChanged);
   }
 }
 
 NS_IMETHODIMP
 FMRadioService::Observe(nsISupports* aSubject,
                         const char* aTopic,
                         const char16_t* aData)
 {
@@ -868,80 +820,80 @@ FMRadioService::Observe(nsISupports* aSu
 
 void
 FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
 {
   mObserverList.Broadcast(aType);
 }
 
 void
-FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
+FMRadioService::Notify(const hal::FMRadioOperationInformation& aInfo)
 {
   switch (aInfo.operation()) {
-    case FM_RADIO_OPERATION_ENABLE:
-      MOZ_ASSERT(IsFMRadioOn());
+    case hal::FM_RADIO_OPERATION_ENABLE:
+      MOZ_ASSERT(hal::IsFMRadioOn());
       MOZ_ASSERT(mState == Disabling || mState == Enabling);
 
       // If we're disabling, disable the radio right now.
       if (mState == Disabling) {
         DoDisable();
         return;
       }
 
       // Fire success callback on the enable request.
       TransitionState(SuccessResponse(), Enabled);
 
       // To make sure the FM app will get the right frequency after the FM
       // radio is enabled, we have to set the frequency first.
-      SetFMRadioFrequency(mPendingFrequencyInKHz);
+      hal::SetFMRadioFrequency(mPendingFrequencyInKHz);
 
       // Bug 949855: enable audio after the FM radio HW is enabled, to make sure
       // 'hw.fm.isAnalog' could be detected as |true| during first time launch.
       // This case is for audio output on analog path, i.e. 'ro.moz.fm.noAnalog'
       // is not |true|.
       EnableAudio(true);
 
       // Update the current frequency without sending the`FrequencyChanged`
       // event, to make sure the FM app will get the right frequency when the
       // `EnabledChange` event is sent.
-      mPendingFrequencyInKHz = GetFMRadioFrequency();
+      mPendingFrequencyInKHz = hal::GetFMRadioFrequency();
       UpdatePowerState();
 
       // The frequency was changed from '0' to some meaningful number, so we
       // should send the `FrequencyChanged` event manually.
       NotifyFMRadioEvent(FrequencyChanged);
 
       if (mRDSEnabled) {
         mRDSEnabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
         if (!mRDSEnabled) {
           NotifyFMRadioEvent(RDSEnabledChanged);
         }
       }
       break;
-    case FM_RADIO_OPERATION_DISABLE:
+    case hal::FM_RADIO_OPERATION_DISABLE:
       MOZ_ASSERT(mState == Disabling);
 
       mPISet = false;
       mPTYSet = false;
       memset(mPSName, 0, sizeof(mPSName));
       memset(mRadiotext, 0, sizeof(mRadiotext));
       TransitionState(SuccessResponse(), Disabled);
       UpdatePowerState();
       break;
-    case FM_RADIO_OPERATION_SEEK:
+    case hal::FM_RADIO_OPERATION_SEEK:
 
       // Seek action might be cancelled by SetFrequency(), we need to check if
       // the current state is Seeking.
       if (mState == Seeking) {
         TransitionState(SuccessResponse(), Enabled);
       }
 
       UpdateFrequency();
       break;
-    case FM_RADIO_OPERATION_TUNE:
+    case hal::FM_RADIO_OPERATION_TUNE:
       UpdateFrequency();
       break;
     default:
       MOZ_CRASH();
   }
 }
 
 /* This is defined by the RDS standard */
@@ -1009,17 +961,17 @@ static const uint16_t sRDSToUnicodeMap[2
   0x00DE, 0x014A, 0x0154, 0x0106, 0x015A, 0x0179, 0x0166, 0x00F0,
 
   // 0xF-
   0x00E3, 0x00E5, 0x00E6, 0x0153, 0x0175, 0x00FD, 0x00F5, 0x00F8,
   0x00FE, 0x014B, 0x0155, 0x0107, 0x015B, 0x017A, 0x0167, 0x0020,
 };
 
 void
-FMRadioService::Notify(const FMRadioRDSGroup& aRDSGroup)
+FMRadioService::Notify(const hal::FMRadioRDSGroup& aRDSGroup)
 {
   uint16_t blocks[4];
   blocks[0] = aRDSGroup.blockA();
   blocks[1] = aRDSGroup.blockB();
   blocks[2] = aRDSGroup.blockC();
   blocks[3] = aRDSGroup.blockD();
 
   /* Bit 11 in block B determines whether this is a type B group. */
@@ -1030,26 +982,28 @@ FMRadioService::Notify(const FMRadioRDSG
     mPI = blocks[0];
     if (!mPISet) {
       mPSNameState = 0;
       mRadiotextState = 0;
       memset(mTempPSName, 0, sizeof(mTempPSName));
       memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
     }
     mPISet = true;
-    NS_DispatchToMainThread(new NotifyRunnable(PIChanged));
+
+    DispatchFMRadioEventToMainThread(PIChanged);
   }
   mLastPI = blocks[0];
 
   /* PTY is also updated using the same logic as PI */
   uint16_t pty = (blocks[1] >> 5) & 0x1F;
   if ((mPTY != pty && pty == mLastPTY) || !mPTYSet) {
     mPTY = pty;
     mPTYSet = true;
-    NS_DispatchToMainThread(new NotifyRunnable(PTYChanged));
+
+    DispatchFMRadioEventToMainThread(PTYChanged);
   }
   mLastPTY = pty;
 
   uint16_t grouptype = blocks[1] >> 11;
   switch (grouptype) {
     case 0: // 0a
     case 1: // 0b
     {
@@ -1070,31 +1024,33 @@ FMRadioService::Notify(const FMRadioRDSG
         break;
       }
 
       mPSNameState = 0;
       if (memcmp(mTempPSName, mPSName, sizeof(mTempPSName))) {
         MutexAutoLock lock(mRDSLock);
         mPSNameSet = true;
         memcpy(mPSName, mTempPSName, sizeof(mTempPSName));
-        NS_DispatchToMainThread(new NotifyRunnable(PSChanged));
+
+        DispatchFMRadioEventToMainThread(PSChanged);
       }
       break;
     }
     case 4: // 2a Radiotext
     {
       uint16_t segmentAddr = (blocks[1] & 0xF);
       bool textAB = blocks[1] & (1 << 5);
       if (textAB != mRadiotextAB) {
         mRadiotextState = 0;
         memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
         mRadiotextAB = textAB;
         MutexAutoLock lock(mRDSLock);
         memset(mRadiotext, 0, sizeof(mRadiotext));
-        NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
+
+        DispatchFMRadioEventToMainThread(RadiotextChanged);
       }
 
       // mRadiotextState is a bitmask that lets us ensure all segments
       // are received before updating the radiotext.
       if (!segmentAddr) {
         mRadiotextState = 1;
       } else {
         mRadiotextState |= 1 << segmentAddr;
@@ -1124,30 +1080,32 @@ FMRadioService::Notify(const FMRadioRDSG
           (mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
           !memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
         break;
       }
 
       MutexAutoLock lock(mRDSLock);
       mRadiotextSet = true;
       memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
-      NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
+
+      DispatchFMRadioEventToMainThread(RadiotextChanged);
       break;
     }
     case 5: // 2b Radiotext
     {
       uint16_t segmentAddr = (blocks[1] & 0xF);
       bool textAB = blocks[1] & (1 << 5);
       if (textAB != mRadiotextAB) {
         mRadiotextState = 0;
         memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
         mRadiotextAB = textAB;
         MutexAutoLock lock(mRDSLock);
         memset(mRadiotext, 0, sizeof(mRadiotext));
-        NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
+
+        DispatchFMRadioEventToMainThread(RadiotextChanged);
       }
 
       if (!segmentAddr) {
         mRadiotextState = 1;
       } else {
         mRadiotextState |= 1 << segmentAddr;
       }
       uint8_t segment[2];
@@ -1172,27 +1130,29 @@ FMRadioService::Notify(const FMRadioRDSG
           (mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
           !memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
         break;
       }
 
       MutexAutoLock lock(mRDSLock);
       mRadiotextSet = true;
       memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
-      NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
+
+      DispatchFMRadioEventToMainThread(RadiotextChanged);
       break;
     }
     case 31: // 15b Fast Tuning and Switching
     {
       uint16_t secondPty = (blocks[3] >> 5) & 0x1F;
       if (pty == mPTY || pty != secondPty) {
         break;
       }
       mPTY = pty;
-      NS_DispatchToMainThread(new NotifyRunnable(PTYChanged));
+
+      DispatchFMRadioEventToMainThread(PTYChanged);
       break;
     }
   }
 
   // Only notify users of raw RDS groups that they're interested in.
   // We always receive DOM_PARSED_RDS_GROUPS when RDS is enabled.
   if (!(mRDSGroupMask & (1 << grouptype))) {
     return;
@@ -1204,33 +1164,34 @@ FMRadioService::Notify(const FMRadioRDSG
   newgroup <<= 16;
   newgroup |= blocks[2];
   newgroup <<= 16;
   newgroup |= blocks[3];
 
   MutexAutoLock lock(mRDSLock);
   mRDSGroup = newgroup;
   mRDSGroupSet = true;
-  NS_DispatchToMainThread(new NotifyRunnable(NewRDSGroup));
+
+  DispatchFMRadioEventToMainThread(NewRDSGroup);
 }
 
 void
 FMRadioService::UpdatePowerState()
 {
-  bool enabled = IsFMRadioOn();
+  bool enabled = hal::IsFMRadioOn();
   if (enabled != mEnabled) {
     mEnabled = enabled;
     NotifyFMRadioEvent(EnabledChanged);
   }
 }
 
 void
 FMRadioService::UpdateFrequency()
 {
-  int32_t frequency = GetFMRadioFrequency();
+  int32_t frequency = hal::GetFMRadioFrequency();
   if (mPendingFrequencyInKHz != frequency) {
     mPendingFrequencyInKHz = frequency;
     NotifyFMRadioEvent(FrequencyChanged);
     mPISet = false;
     mPTYSet = false;
     memset(mPSName, 0, sizeof(mPSName));
     memset(mRadiotext, 0, sizeof(mRadiotext));
     mRDSGroupSet = false;
--- a/dom/fmradio/FMRadioService.h
+++ b/dom/fmradio/FMRadioService.h
@@ -191,16 +191,20 @@ public:
 
   virtual void EnableAudio(bool aAudioEnabled) override;
 
   /* FMRadioObserver */
   void Notify(const hal::FMRadioOperationInformation& aInfo) override;
   /* FMRadioRDSObserver */
   void Notify(const hal::FMRadioRDSGroup& aRDSGroup) override;
 
+  void EnableFMRadio();
+  void DisableFMRadio();
+  void DispatchFMRadioEventToMainThread(enum FMRadioEventType aType);
+
   NS_DECL_NSIOBSERVER
 
 protected:
   FMRadioService();
   virtual ~FMRadioService();
 
 private:
   int32_t RoundFrequency(double aFrequencyInMHz);
--- a/dom/html/ImageDocument.cpp
+++ b/dom/html/ImageDocument.cpp
@@ -94,17 +94,17 @@ ImageListener::OnStartRequest(nsIRequest
 
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> channelPrincipal;
   if (secMan) {
     secMan->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
   }
 
   int16_t decision = nsIContentPolicy::ACCEPT;
-  nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE,
+  nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                              channelURI,
                                              channelPrincipal,
                                              domWindow->GetFrameElementInternal(),
                                              mimeType,
                                              nullptr,
                                              &decision,
                                              nsContentUtils::GetContentPolicy(),
                                              secMan);
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -47,19 +47,16 @@ enum class MediaDecoderEventVisibility :
  */
 class AbstractMediaDecoder : public nsIObserver
 {
 public:
   // Returns the monitor for other threads to synchronise access to
   // state.
   virtual ReentrantMonitor& GetReentrantMonitor() = 0;
 
-  // Returns true if the decoder is shut down.
-  virtual bool IsShutdown() const = 0;
-
   // A special version of the above for the ogg decoder that is allowed to be
   // called cross-thread.
   virtual bool IsOggDecoderShutdown() { return false; }
 
   virtual bool OnStateMachineTaskQueue() const = 0;
 
   virtual bool OnDecodeTaskQueue() const = 0;
 
@@ -88,16 +85,23 @@ public:
       NS_NewRunnableMethodWithArg<int64_t>(this, &AbstractMediaDecoder::UpdateEstimatedMediaDuration,
                                            aDuration);
     NS_DispatchToMainThread(r);
   }
 
   // Set the media as being seekable or not.
   virtual void SetMediaSeekable(bool aMediaSeekable) = 0;
 
+  void DispatchSetMediaSeekable(bool aMediaSeekable)
+  {
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
+      this, &AbstractMediaDecoder::SetMediaSeekable, aMediaSeekable);
+    NS_DispatchToMainThread(r);
+  }
+
   virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
   virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;
 
   // Return true if the media layer supports seeking.
   virtual bool IsTransportSeekable() = 0;
 
   // Return true if the transport layer supports seeking.
   virtual bool IsMediaSeekable() = 0;
--- a/dom/media/IdpSandbox.jsm
+++ b/dom/media/IdpSandbox.jsm
@@ -45,17 +45,17 @@ ResourceLoader.load = function(uri, doc)
   return new Promise((resolve, reject) => {
     let listener = new ResourceLoader(resolve, reject);
     let ioService = Cc['@mozilla.org/network/io-service;1']
       .getService(Ci.nsIIOService);
     let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     // the '2' identifies this as a script load
     let ioChannel = ioService.newChannelFromURI2(uri, doc, doc.nodePrincipal,
                                                  systemPrincipal, 0,
-                                                 Ci.nsIContentPolicy.TYPE_SCRIPT);
+                                                 Ci.nsIContentPolicy.TYPE_INTERNAL_SCRIPT);
 
     ioChannel.loadGroup = doc.documentLoadGroup.QueryInterface(Ci.nsILoadGroup);
     ioChannel.notificationCallbacks = new RedirectHttpsOnly();
     ioChannel.asyncOpen(listener, null);
   });
 };
 
 ResourceLoader.prototype = {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -212,38 +212,26 @@ void MediaDecoder::UpdateDormantState(bo
   if (prevDormant == mIsDormant) {
     // No update to dormant state
     return;
   }
 
   if (mIsDormant) {
     DECODER_LOG("UpdateDormantState() entering DORMANT state");
     // enter dormant state
-    RefPtr<nsRunnable> event =
-      NS_NewRunnableMethodWithArg<bool>(
-        mDecoderStateMachine,
-        &MediaDecoderStateMachine::SetDormant,
-        true);
-    mDecoderStateMachine->OwnerThread()->Dispatch(event.forget());
-
+    mDecoderStateMachine->DispatchSetDormant(true);
     if (IsEnded()) {
       mWasEndedWhenEnteredDormant = true;
     }
     mNextState = mPlayState;
     ChangeState(PLAY_STATE_LOADING);
   } else {
     DECODER_LOG("UpdateDormantState() leaving DORMANT state");
     // exit dormant state
-    // trigger to state machine.
-    RefPtr<nsRunnable> event =
-      NS_NewRunnableMethodWithArg<bool>(
-        mDecoderStateMachine,
-        &MediaDecoderStateMachine::SetDormant,
-        false);
-    mDecoderStateMachine->OwnerThread()->Dispatch(event.forget());
+    mDecoderStateMachine->DispatchSetDormant(false);
   }
 }
 
 void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aClosure);
   MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
@@ -346,17 +334,16 @@ bool MediaDecoder::IsInfinite()
   return mInfiniteStream;
 }
 
 MediaDecoder::MediaDecoder() :
   mWatchManager(this, AbstractThread::MainThread()),
   mDormantSupported(false),
   mLogicalPosition(0.0),
   mDuration(std::numeric_limits<double>::quiet_NaN()),
-  mMediaSeekable(true),
   mReentrantMonitor("media.decoder"),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mOwner(nullptr),
   mPlaybackStatistics(new MediaChannelStatistics()),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
@@ -402,17 +389,19 @@ MediaDecoder::MediaDecoder() :
                     "MediaDecoder::mLogicallySeeking (Canonical)"),
   mSameOriginMedia(AbstractThread::MainThread(), false,
                    "MediaDecoder::mSameOriginMedia (Canonical)"),
   mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
                           "MediaDecoder::mPlaybackBytesPerSecond (Canonical)"),
   mPlaybackRateReliable(AbstractThread::MainThread(), true,
                         "MediaDecoder::mPlaybackRateReliable (Canonical)"),
   mDecoderPosition(AbstractThread::MainThread(), 0,
-                   "MediaDecoder::mDecoderPosition (Canonical)")
+                   "MediaDecoder::mDecoderPosition (Canonical)"),
+  mMediaSeekable(AbstractThread::MainThread(), true,
+                 "MediaDecoder::mMediaSeekable (Canonical)")
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 
   //
@@ -612,19 +601,18 @@ nsresult MediaDecoder::Seek(double aTime
   }
   return NS_OK;
 }
 
 void MediaDecoder::CallSeek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSeekRequest.DisconnectIfExists();
-  mSeekRequest.Begin(InvokeAsync(mDecoderStateMachine->OwnerThread(),
-                                 mDecoderStateMachine.get(), __func__,
-                                 &MediaDecoderStateMachine::Seek, aTarget)
+  mSeekRequest.Begin(
+    mDecoderStateMachine->InvokeSeek(aTarget)
     ->Then(AbstractThread::MainThread(), __func__, this,
            &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
 }
 
 double MediaDecoder::GetCurrentTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mLogicalPosition;
@@ -1105,31 +1093,32 @@ void MediaDecoder::UpdateEstimatedMediaD
       mozilla::Abs(mEstimatedDuration.Ref().ref().ToMicroseconds() - aDuration) < ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
     return;
   }
 
   mEstimatedDuration = Some(TimeUnit::FromMicroseconds(aDuration));
 }
 
 void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
+  MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mMediaSeekable = aMediaSeekable;
 }
 
 bool
 MediaDecoder::IsTransportSeekable()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GetResource()->IsTransportSeekable();
 }
 
 bool MediaDecoder::IsMediaSeekable()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(GetStateMachine(), false);
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return mMediaSeekable;
 }
 
 media::TimeIntervals MediaDecoder::GetSeekable()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // We can seek in buffered range if the media is seekable. Also, we can seek
   // in unbuffered ranges if the transport level is seekable (local file or the
@@ -1304,29 +1293,20 @@ void MediaDecoder::NotifyDataArrived(uin
 MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
   return mDecoderStateMachine;
 }
 
 void
 MediaDecoder::NotifyWaitingForResourcesStatusChanged()
 {
   if (mDecoderStateMachine) {
-    RefPtr<nsRunnable> task =
-      NS_NewRunnableMethod(mDecoderStateMachine,
-                           &MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged);
-    mDecoderStateMachine->OwnerThread()->Dispatch(task.forget());
+    mDecoderStateMachine->DispatchWaitingForResourcesStatusChanged();
   }
 }
 
-bool MediaDecoder::IsShutdown() const {
-  MOZ_ASSERT(NS_IsMainThread());
-  NS_ENSURE_TRUE(GetStateMachine(), true);
-  return mStateMachineIsShutdown;
-}
-
 // Drop reference to state machine.  Only called during shutdown dance.
 void MediaDecoder::BreakCycles() {
   SetStateMachine(nullptr);
 }
 
 MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
 {
   return mOwner;
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -525,19 +525,16 @@ public:
 
   MediaDecoderStateMachine* GetStateMachine() { return mDecoderStateMachine; }
   void SetStateMachine(MediaDecoderStateMachine* aStateMachine);
 
   // Returns the monitor for other threads to synchronise access to
   // state.
   ReentrantMonitor& GetReentrantMonitor() override;
 
-  // Returns true if the decoder is shut down
-  bool IsShutdown() const final override;
-
   // Constructs the time ranges representing what segments of the media
   // are buffered and playable.
   virtual media::TimeIntervals GetBuffered();
 
   // Returns the size, in bytes, of the heap memory used by the currently
   // queued decoded video and audio data.
   size_t SizeOfVideoQueue();
   size_t SizeOfAudioQueue();
@@ -796,19 +793,16 @@ protected:
   // This corresponds to the "current position" in HTML5.
   // We allow omx subclasses to substitute an alternative current position for
   // usage with the audio offload player.
   virtual int64_t CurrentPosition() { return mCurrentPosition; }
 
   // Official duration of the media resource as observed by script.
   double mDuration;
 
-  // True if the media is seekable (i.e. supports random access).
-  bool mMediaSeekable;
-
   /******
    * The following member variables can be accessed from any thread.
    ******/
 
   // Media data resource.
   nsRefPtr<MediaResource> mResource;
 
 private:
@@ -996,16 +990,19 @@ protected:
   Canonical<bool> mPlaybackRateReliable;
 
   // Current decoding position in the stream. This is where the decoder
   // is up to consuming the stream. This is not adjusted during decoder
   // seek operations, but it's updated at the end when we start playing
   // back again.
   Canonical<int64_t> mDecoderPosition;
 
+  // True if the media is seekable (i.e. supports random access).
+  Canonical<bool> mMediaSeekable;
+
 public:
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
   AbstractCanonical<double>* CanonicalVolume() {
     return &mVolume;
   }
   AbstractCanonical<double>* CanonicalPlaybackRate() {
     return &mPlaybackRate;
   }
@@ -1034,13 +1031,16 @@ public:
     return &mPlaybackBytesPerSecond;
   }
   AbstractCanonical<bool>* CanonicalPlaybackRateReliable() {
     return &mPlaybackRateReliable;
   }
   AbstractCanonical<int64_t>* CanonicalDecoderPosition() {
     return &mDecoderPosition;
   }
+  AbstractCanonical<bool>* CanonicalMediaSeekable() {
+    return &mMediaSeekable;
+  }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -244,16 +244,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mSameOriginMedia(mTaskQueue, false,
                    "MediaDecoderStateMachine::mSameOriginMedia (Mirror)"),
   mPlaybackBytesPerSecond(mTaskQueue, 0.0,
                           "MediaDecoderStateMachine::mPlaybackBytesPerSecond (Mirror)"),
   mPlaybackRateReliable(mTaskQueue, true,
                         "MediaDecoderStateMachine::mPlaybackRateReliable (Mirror)"),
   mDecoderPosition(mTaskQueue, 0,
                    "MediaDecoderStateMachine::mDecoderPosition (Mirror)"),
+  mMediaSeekable(mTaskQueue, true,
+                 "MediaDecoderStateMachine::mMediaSeekable (Mirror)"),
   mDuration(mTaskQueue, NullableTimeUnit(),
             "MediaDecoderStateMachine::mDuration (Canonical"),
   mIsShutdown(mTaskQueue, false,
               "MediaDecoderStateMachine::mIsShutdown (Canonical)"),
   mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"),
   mCurrentPosition(mTaskQueue, 0,
                    "MediaDecoderStateMachine::mCurrentPosition (Canonical)"),
@@ -329,16 +331,17 @@ MediaDecoderStateMachine::Initialization
   mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(mDecoder->CanonicalVolume());
   mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
   mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
   mSameOriginMedia.Connect(mDecoder->CanonicalSameOriginMedia());
   mPlaybackBytesPerSecond.Connect(mDecoder->CanonicalPlaybackBytesPerSecond());
   mPlaybackRateReliable.Connect(mDecoder->CanonicalPlaybackRateReliable());
   mDecoderPosition.Connect(mDecoder->CanonicalDecoderPosition());
+  mMediaSeekable.Connect(mDecoder->CanonicalMediaSeekable());
 
   // Initialize watchers.
   mWatchManager.Watch(mBuffered, &MediaDecoderStateMachine::BufferedRangeUpdated);
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
@@ -1226,17 +1229,26 @@ void MediaDecoderStateMachine::Recompute
   if (duration < mObservedDuration.Ref()) {
     duration = mObservedDuration;
   }
 
   MOZ_ASSERT(duration.ToMicroseconds() >= 0);
   mDuration = Some(duration);
 }
 
-void MediaDecoderStateMachine::SetDormant(bool aDormant)
+void
+MediaDecoderStateMachine::DispatchSetDormant(bool aDormant)
+{
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
+    this, &MediaDecoderStateMachine::SetDormant, aDormant);
+  OwnerThread()->Dispatch(r.forget());
+}
+
+void
+MediaDecoderStateMachine::SetDormant(bool aDormant)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   if (IsShutdown()) {
     return;
   }
 
@@ -1374,17 +1386,26 @@ void MediaDecoderStateMachine::StartDeco
   mIsVideoPrerolling = !DonePrerollingVideo();
 
   // Ensure that we've got tasks enqueued to decode data if we need to.
   DispatchDecodeTasksIfNeeded();
 
   ScheduleStateMachine();
 }
 
-void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
+void
+MediaDecoderStateMachine::DispatchWaitingForResourcesStatusChanged()
+{
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
+    this, &MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged);
+  OwnerThread()->Dispatch(r.forget());
+}
+
+void
+MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
 
   if (mState == DECODER_STATE_WAIT_FOR_RESOURCES) {
     // Try again.
     SetState(DECODER_STATE_DECODING_NONE);
@@ -1478,17 +1499,17 @@ MediaDecoderStateMachine::Seek(SeekTarge
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   if (IsShutdown()) {
     return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
   }
 
   // We need to be able to seek both at a transport level and at a media level
   // to seek.
-  if (!mDecoder->IsMediaSeekable()) {
+  if (!mMediaSeekable) {
     DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
     return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
   }
 
   NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
                "We should have got duration already");
 
   if (mState < DECODER_STATE_DECODING ||
@@ -1504,16 +1525,23 @@ MediaDecoderStateMachine::Seek(SeekTarge
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
   SetState(DECODER_STATE_SEEKING);
   ScheduleStateMachine();
 
   return mPendingSeek.mPromise.Ensure(__func__);
 }
 
+nsRefPtr<MediaDecoder::SeekPromise>
+MediaDecoderStateMachine::InvokeSeek(SeekTarget aTarget)
+{
+  return InvokeAsync(OwnerThread(), this, __func__,
+                     &MediaDecoderStateMachine::Seek, aTarget);
+}
+
 void MediaDecoderStateMachine::StopMediaSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (mMediaSink->IsStarted()) {
     DECODER_LOG("Stop MediaSink");
     mMediaSink->Stop();
@@ -1927,17 +1955,17 @@ MediaDecoderStateMachine::OnMetadataRead
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mMetadataRequest.Complete();
 
   // Set mode to PLAYBACK after reading metadata.
   mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
-  mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
+  mDecoder->DispatchSetMediaSeekable(mReader->IsMediaSeekable());
   mInfo = aMetadata->mInfo;
   mMetadataTags = aMetadata->mTags.forget();
   nsRefPtr<MediaDecoderStateMachine> self = this;
 
   // Set up the start time rendezvous if it doesn't already exist (which is
   // generally the case, unless we're coming out of dormant mode).
   if (!mStartTimeRendezvous) {
     mStartTimeRendezvous = new StartTimeRendezvous(OwnerThread(), HasAudio(), HasVideo(),
@@ -2062,17 +2090,17 @@ MediaDecoderStateMachine::FinishDecodeFi
 
   // If we don't know the duration by this point, we assume infinity, per spec.
   if (mDuration.Ref().isNothing()) {
     mDuration = Some(TimeUnit::FromInfinity());
   }
 
   DECODER_LOG("Media duration %lld, "
               "transportSeekable=%d, mediaSeekable=%d",
-              Duration().ToMicroseconds(), mResource->IsTransportSeekable(), mDecoder->IsMediaSeekable());
+              Duration().ToMicroseconds(), mResource->IsTransportSeekable(), mMediaSeekable.Ref());
 
   if (HasAudio() && !HasVideo() && !mSentFirstFrameLoadedEvent) {
     // We're playing audio only. We don't need to worry about slow video
     // decodes causing audio underruns, so don't buffer so much audio in
     // order to reduce memory usage.
     mAmpleAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
     mLowAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
     mQuickBufferingLowDataThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
@@ -2231,16 +2259,17 @@ MediaDecoderStateMachine::FinishShutdown
   mLogicallySeeking.DisconnectIfConnected();
   mVolume.DisconnectIfConnected();
   mLogicalPlaybackRate.DisconnectIfConnected();
   mPreservesPitch.DisconnectIfConnected();
   mSameOriginMedia.DisconnectIfConnected();
   mPlaybackBytesPerSecond.DisconnectIfConnected();
   mPlaybackRateReliable.DisconnectIfConnected();
   mDecoderPosition.DisconnectIfConnected();
+  mMediaSeekable.DisconnectIfConnected();
 
   mDuration.DisconnectAll();
   mIsShutdown.DisconnectAll();
   mNextFrameStatus.DisconnectAll();
   mCurrentPosition.DisconnectAll();
   mPlaybackOffset.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -150,80 +150,147 @@ public:
     DECODER_STATE_SHUTDOWN,
     DECODER_STATE_ERROR
   };
 
   void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
   // Remove an output stream added with AddOutputStream.
   void RemoveOutputStream(MediaStream* aStream);
 
+  // Seeks to the decoder to aTarget asynchronously.
+  nsRefPtr<MediaDecoder::SeekPromise> InvokeSeek(SeekTarget aTarget);
+
   // Set/Unset dormant state.
-  void SetDormant(bool aDormant);
+  void DispatchSetDormant(bool aDormant);
+
+  void DispatchShutdown();
+
+  void DispatchStartBuffering()
+  {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartBuffering);
+    OwnerThread()->Dispatch(runnable.forget());
+  }
+
+  void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
+  {
+    mReader->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
+  }
+
+  // Called when the reader may have acquired the hardware resources required
+  // to begin decoding.
+  void DispatchWaitingForResourcesStatusChanged();
+
+  // Notifies the state machine that should minimize the number of samples
+  // decoded we preroll, until playback starts. The first time playback starts
+  // the state machine is free to return to prerolling normally. Note
+  // "prerolling" in this context refers to when we decode and buffer decoded
+  // samples in advance of when they're needed for playback.
+  void DispatchMinimizePrerollUntilPlaybackStarts()
+  {
+    nsRefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
+    {
+      MOZ_ASSERT(self->OnTaskQueue());
+      ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
+      self->mMinimizePreroll = true;
+
+      // Make sure that this arrives before playback starts, otherwise this won't
+      // have the intended effect.
+      MOZ_DIAGNOSTIC_ASSERT(self->mPlayState == MediaDecoder::PLAY_STATE_LOADING);
+    });
+    OwnerThread()->Dispatch(r.forget());
+  }
+
+  // Set the media fragment end time. aEndTime is in microseconds.
+  void DispatchSetFragmentEndTime(int64_t aEndTime)
+  {
+    nsRefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aEndTime] () {
+      self->mFragmentEndTime = aEndTime;
+    });
+    OwnerThread()->Dispatch(r.forget());
+  }
+
+  // Drop reference to decoder.  Only called during shutdown dance.
+  void BreakCycles() {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (mReader) {
+      mReader->BreakCycles();
+    }
+    mResource = nullptr;
+    mDecoder = nullptr;
+  }
 
   TimedMetadataEventSource& TimedMetadataEvent() {
     return mMetadataManager.TimedMetadataEvent();
   }
 
+  // Immutable after construction - may be called on any thread.
+  bool IsRealTime() const { return mRealTime; }
+
+  // Functions used by assertions to ensure we're calling things
+  // on the appropriate threads.
+  bool OnDecodeTaskQueue() const;
+
+  bool OnTaskQueue() const;
+
+  size_t SizeOfVideoQueue() {
+    if (mReader) {
+      return mReader->SizeOfVideoQueueInBytes();
+    }
+    return 0;
+  }
+
+  size_t SizeOfAudioQueue() {
+    if (mReader) {
+      return mReader->SizeOfAudioQueueInBytes();
+    }
+    return 0;
+  }
+
 private:
   // Initialization that needs to happen on the task queue. This is the first
   // task that gets run on the task queue, and is dispatched from the MDSM
   // constructor immediately after the task queue is created.
   void InitializationTask();
 
+  void SetDormant(bool aDormant);
+
   void SetAudioCaptured(bool aCaptured);
 
+  void NotifyWaitingForResourcesStatusChanged();
+
+  nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
+
   void Shutdown();
-public:
-
-  void DispatchShutdown();
 
   void FinishShutdown();
 
-  // Immutable after construction - may be called on any thread.
-  bool IsRealTime() const { return mRealTime; }
-
-  // Functions used by assertions to ensure we're calling things
-  // on the appropriate threads.
-  bool OnDecodeTaskQueue() const;
-  bool OnTaskQueue() const;
-
-  // Seeks to the decoder to aTarget asynchronously.
-  // Must be called on the state machine thread.
-  nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
-
   // Clear the flag indicating that a playback position change event
   // is currently queued. This is called from the main thread and must
   // be called with the decode monitor held.
   void ClearPositionChangeFlag();
 
   // Update the playback position. This can result in a timeupdate event
   // and an invalidate of the frame being dispatched asynchronously if
   // there is no such event currently queued.
   // Only called on the decoder thread. Must be called with
   // the decode monitor held.
   void UpdatePlaybackPosition(int64_t aTime);
 
-private:
   // Causes the state machine to switch to buffering state, and to
   // immediately stop playback and buffer downloaded data. Called on
   // the state machine thread.
   void StartBuffering();
 
   bool CanPlayThrough();
 
   MediaStatistics GetStatistics();
 
-public:
-  void DispatchStartBuffering()
-  {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartBuffering);
-    OwnerThread()->Dispatch(runnable.forget());
-  }
-
   // This is called on the state machine thread and audio thread.
   // The decoder monitor must be obtained before calling this.
   bool HasAudio() const {
     MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return mInfo.HasAudio();
   }
 
@@ -247,35 +314,16 @@ public:
 
   // Must be called with the decode monitor held.
   bool IsSeeking() const {
     MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return mState == DECODER_STATE_SEEKING;
   }
 
-  size_t SizeOfVideoQueue() {
-    if (mReader) {
-      return mReader->SizeOfVideoQueueInBytes();
-    }
-    return 0;
-  }
-
-  size_t SizeOfAudioQueue() {
-    if (mReader) {
-      return mReader->SizeOfAudioQueueInBytes();
-    }
-    return 0;
-  }
-
-  void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates)
-  {
-    mReader->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates);
-  }
-
   // Returns the state machine task queue.
   TaskQueue* OwnerThread() const { return mTaskQueue; }
 
   // Calls ScheduleStateMachine() after taking the decoder lock. Also
   // notifies the decoder thread in case it's waiting on the decoder lock.
   void ScheduleStateMachineWithLockAndWakeDecoder();
 
   // Schedules the shared state machine thread to run the state machine.
@@ -301,74 +349,29 @@ public:
     MOZ_ASSERT(OnTaskQueue());
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDelayedScheduler.CompleteRequest();
     ScheduleStateMachine();
   }
 
   void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); }
 
-  // Set the media fragment end time. aEndTime is in microseconds.
-  void DispatchSetFragmentEndTime(int64_t aEndTime)
-  {
-    nsRefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aEndTime] () {
-      self->mFragmentEndTime = aEndTime;
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
-  // Drop reference to decoder.  Only called during shutdown dance.
-  void BreakCycles() {
-    MOZ_ASSERT(NS_IsMainThread());
-    if (mReader) {
-      mReader->BreakCycles();
-    }
-    mResource = nullptr;
-    mDecoder = nullptr;
-  }
-
   // Discard audio/video data that are already played by MSG.
   void DiscardStreamData();
   bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs);
   bool HaveEnoughDecodedVideo();
 
   // Returns true if the state machine has shutdown or is in the process of
   // shutting down. The decoder monitor must be held while calling this.
   bool IsShutdown();
 
   // Returns true if we're currently playing. The decoder monitor must
   // be held.
   bool IsPlaying() const;
 
-  // Called when the reader may have acquired the hardware resources required
-  // to begin decoding.
-  void NotifyWaitingForResourcesStatusChanged();
-
-  // Notifies the state machine that should minimize the number of samples
-  // decoded we preroll, until playback starts. The first time playback starts
-  // the state machine is free to return to prerolling normally. Note
-  // "prerolling" in this context refers to when we decode and buffer decoded
-  // samples in advance of when they're needed for playback.
-  void DispatchMinimizePrerollUntilPlaybackStarts()
-  {
-    nsRefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
-    {
-      MOZ_ASSERT(self->OnTaskQueue());
-      ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
-      self->mMinimizePreroll = true;
-
-      // Make sure that this arrives before playback starts, otherwise this won't
-      // have the intended effect.
-      MOZ_DIAGNOSTIC_ASSERT(self->mPlayState == MediaDecoder::PLAY_STATE_LOADING);
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
   void OnAudioDecoded(MediaData* aAudioSample);
   void OnVideoDecoded(MediaData* aVideoSample);
   void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
   void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
   {
     MOZ_ASSERT(OnTaskQueue());
     OnNotDecoded(MediaData::AUDIO_DATA, aReason);
   }
@@ -1294,16 +1297,19 @@ private:
   Mirror<double> mPlaybackBytesPerSecond;
 
   // True if mPlaybackBytesPerSecond is a reliable estimate.
   Mirror<bool> mPlaybackRateReliable;
 
   // Current decoding position in the stream.
   Mirror<int64_t> mDecoderPosition;
 
+  // True if the media is seekable (i.e. supports random access).
+  Mirror<bool> mMediaSeekable;
+
   // Duration of the media. This is guaranteed to be non-null after we finish
   // decoding the first frame.
   Canonical<media::NullableTimeUnit> mDuration;
 
   // Whether we're currently in or transitioning to shutdown state.
   Canonical<bool> mIsShutdown;
 
   // The status of our next frame. Mirrored on the main thread and used to
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2360,18 +2360,18 @@ void
 MediaInputPort::Disconnect()
 {
   NS_ASSERTION(!mSource == !mDest,
                "mSource must either both be null or both non-null");
   if (!mSource)
     return;
 
   mSource->RemoveConsumer(this);
+  mDest->RemoveInput(this);
   mSource = nullptr;
-  mDest->RemoveInput(this);
   mDest = nullptr;
 
   GraphImpl()->SetStreamOrderDirty();
 }
 
 MediaInputPort::InputInterval
 MediaInputPort::GetNextInputInterval(GraphTime aTime)
 {
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -46,19 +46,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequests[i].mPromise)
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 MediaKeySystemAccessManager::MediaKeySystemAccessManager(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
   , mAddedObservers(false)
-#ifdef XP_WIN
   , mTrialCreator(new GMPVideoDecoderTrialCreator())
-#endif
 {
 }
 
 MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
 {
   Shutdown();
 }
 
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -22,17 +22,16 @@ using media::TimeIntervals;
 // Gap allowed between frames. Due to inaccuracies in determining buffer end
 // frames (Bug 1065207). This value is based on the end of frame
 // default value used in Blink, kDefaultBufferDurationInMs.
 #define EOS_FUZZ_US 125000
 
 MediaSourceDemuxer::MediaSourceDemuxer()
   : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
                              /* aSupportsTailDispatch = */ true))
-  , mInitDone(false)
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::Init()
 {
@@ -41,36 +40,33 @@ MediaSourceDemuxer::Init()
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::AttemptInit()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (ScanSourceBuffersForContent()) {
-    mInitDone = true;
     return InitPromise::CreateAndResolve(NS_OK, __func__);
   }
 
   nsRefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
 
   return p;
 }
 
 void MediaSourceDemuxer::NotifyDataArrived(uint32_t aLength, int64_t aOffset)
 {
   nsRefPtr<MediaSourceDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
-      if (self->mInitDone) {
+      if (self->mInitPromise.IsEmpty()) {
         return;
       }
-      MOZ_ASSERT(!self->mInitPromise.IsEmpty());
       if (self->ScanSourceBuffersForContent()) {
-        self->mInitDone = true;
         self->mInitPromise.ResolveIfExists(NS_OK, __func__);
       }
     });
   GetTaskQueue()->Dispatch(task.forget());
 }
 
 bool
 MediaSourceDemuxer::ScanSourceBuffersForContent()
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -69,17 +69,16 @@ private:
   }
 
   RefPtr<TaskQueue> mTaskQueue;
   nsTArray<nsRefPtr<MediaSourceTrackDemuxer>> mDemuxers;
 
   nsTArray<nsRefPtr<TrackBuffersManager>> mSourceBuffers;
 
   MozPromiseHolder<InitPromise> mInitPromise;
-  bool mInitDone;
 
   // Monitor to protect members below across multiple threads.
   mutable Monitor mMonitor;
   nsRefPtr<TrackBuffersManager> mAudioTrack;
   nsRefPtr<TrackBuffersManager> mVideoTrack;
   MediaInfo mInfo;
 };
 
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -715,20 +715,17 @@ bool OggReader::DecodeAudioData()
   return true;
 }
 
 void OggReader::SetChained(bool aIsChained) {
   {
     ReentrantMonitorAutoEnter mon(mMonitor);
     mIsChained = aIsChained;
   }
-  {
-    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    mDecoder->SetMediaSeekable(false);
-  }
+  mDecoder->DispatchSetMediaSeekable(false);
 }
 
 bool OggReader::ReadOggChain()
 {
   bool chained = false;
   OpusState* newOpusState = nullptr;
   VorbisState* newVorbisState = nullptr;
   nsAutoPtr<MetadataTags> tags;
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -111,22 +111,17 @@ MediaOmxCommonDecoder::PauseStateMachine
   if (mShuttingDown) {
     return;
   }
 
   if (!GetStateMachine()) {
     return;
   }
   // enter dormant state
-  RefPtr<nsRunnable> event =
-    NS_NewRunnableMethodWithArg<bool>(
-      GetStateMachine(),
-      &MediaDecoderStateMachine::SetDormant,
-      true);
-  GetStateMachine()->OwnerThread()->Dispatch(event.forget());
+  GetStateMachine()->DispatchSetDormant(true);
 }
 
 void
 MediaOmxCommonDecoder::ResumeStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   DECODER_LOG(LogLevel::Debug, ("%s current time %f", __PRETTY_FUNCTION__, mLogicalPosition));
@@ -140,32 +135,22 @@ MediaOmxCommonDecoder::ResumeStateMachin
   }
 
   mFallbackToStateMachine = true;
   mAudioOffloadPlayer = nullptr;
   SeekTarget target = SeekTarget(mLogicalPosition,
                                  SeekTarget::Accurate,
                                  MediaDecoderEventVisibility::Suppressed);
   // Call Seek of MediaDecoderStateMachine to suppress seek events.
-  RefPtr<nsRunnable> event =
-    NS_NewRunnableMethodWithArg<SeekTarget>(
-      GetStateMachine(),
-      &MediaDecoderStateMachine::Seek,
-      target);
-  GetStateMachine()->OwnerThread()->Dispatch(event.forget());
+  GetStateMachine()->InvokeSeek(target);
 
   mNextState = mPlayState;
   ChangeState(PLAY_STATE_LOADING);
   // exit dormant state
-  event =
-    NS_NewRunnableMethodWithArg<bool>(
-      GetStateMachine(),
-      &MediaDecoderStateMachine::SetDormant,
-      false);
-  GetStateMachine()->OwnerThread()->Dispatch(event.forget());
+  GetStateMachine()->DispatchSetDormant(false);
   UpdateLogicalPosition();
 }
 
 void
 MediaOmxCommonDecoder::AudioOffloadTearDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
--- a/dom/media/webaudio/AnalyserNode.cpp
+++ b/dom/media/webaudio/AnalyserNode.cpp
@@ -60,25 +60,49 @@ public:
 
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioBlock& aInput,
                             AudioBlock* aOutput,
                             bool* aFinished) override
   {
     *aOutput = aInput;
 
+    if (aInput.IsNull()) {
+      // If AnalyserNode::mChunks has only null chunks, then there is no need
+      // to send further null chunks.
+      if (mChunksToProcess <= 0) {
+        if (mChunksToProcess != INT32_MIN) {
+          mChunksToProcess = INT32_MIN;
+          aStream->CheckForInactive();
+        }
+        return;
+      }
+
+      --mChunksToProcess;
+    } else {
+      // This many null chunks will be required to empty AnalyserNode::mChunks.
+      mChunksToProcess = CHUNK_COUNT;
+    }
+
     nsRefPtr<TransferBuffer> transfer =
       new TransferBuffer(aStream, aInput.AsAudioChunk());
     NS_DispatchToMainThread(transfer);
   }
 
+  virtual bool IsActive() const override
+  {
+    return mChunksToProcess != INT32_MIN;
+  }
+
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
+
+  int32_t mChunksToProcess = INT32_MIN;
 };
 
 AnalyserNode::AnalyserNode(AudioContext* aContext)
   : AudioNode(aContext,
               1,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
--- a/dom/media/webaudio/AudioBlock.h
+++ b/dom/media/webaudio/AudioBlock.h
@@ -18,16 +18,17 @@ namespace mozilla {
  *
  * Use on graph thread only.
  */
 class AudioBlock : private AudioChunk
 {
 public:
   AudioBlock() {
     mDuration = WEBAUDIO_BLOCK_SIZE;
+    mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
   // No effort is made in constructors to ensure that mBufferIsDownstreamRef
   // is set because the block is expected to be a temporary and so the
   // reference will be released before the next iteration.
   // The custom copy constructor is required so as not to set
   // mBufferIsDownstreamRef without notifying AudioBlockBuffer.
   AudioBlock(const AudioBlock& aBlock) : AudioChunk(aBlock.AsAudioChunk()) {}
   explicit AudioBlock(const AudioChunk& aChunk)
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -106,17 +106,21 @@ public:
       break;
     default:
       NS_ERROR("Bad AudioBufferSourceNodeEngine double parameter.");
     };
   }
   virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
   {
     switch (aIndex) {
-    case AudioBufferSourceNode::SAMPLE_RATE: mBufferSampleRate = aParam; break;
+    case AudioBufferSourceNode::SAMPLE_RATE:
+      MOZ_ASSERT(aParam > 0);
+      mBufferSampleRate = aParam;
+      mSource->SetActive();
+      break;
     case AudioBufferSourceNode::BUFFERSTART:
       MOZ_ASSERT(aParam >= 0);
       if (mBufferPosition == 0) {
         mBufferPosition = aParam;
       }
       break;
     case AudioBufferSourceNode::BUFFEREND:
       MOZ_ASSERT(aParam >= 0);
@@ -461,22 +465,31 @@ public:
                             bool* aFinished) override
   {
     if (mBufferSampleRate == 0) {
       // start() has not yet been called or no buffer has yet been set
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
+    StreamTime streamPosition = aStream->GetCurrentPosition();
+    // We've finished if we've gone past mStop, or if we're past mDuration when
+    // looping is disabled.
+    if (streamPosition >= mStop ||
+        (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
+      *aFinished = true;
+      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+      return;
+    }
+
     uint32_t channels = mBuffer ? mBuffer->GetChannels() : 0;
 
     UpdateSampleRateIfNeeded(channels);
 
     uint32_t written = 0;
-    StreamTime streamPosition = aStream->GetCurrentPosition();
     while (written < WEBAUDIO_BLOCK_SIZE) {
       if (mStop != STREAM_TIME_MAX &&
           streamPosition >= mStop) {
         FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
         continue;
       }
       if (streamPosition < mBeginProcessing) {
         FillWithZeroes(aOutput, channels, &written, &streamPosition,
@@ -494,23 +507,22 @@ public:
       } else {
         if (mBufferPosition < mBufferEnd || mRemainingResamplerTail) {
           CopyFromBuffer(aStream, aOutput, channels, &written, &streamPosition, mBufferEnd);
         } else {
           FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
         }
       }
     }
+  }
 
-    // We've finished if we've gone past mStop, or if we're past mDuration when
-    // looping is disabled.
-    if (streamPosition >= mStop ||
-        (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
-      *aFinished = true;
-    }
+  virtual bool IsActive() const override
+  {
+    // Whether buffer has been set and start() has been called.
+    return mBufferSampleRate != 0;
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     // Not owned:
     // - mBuffer - shared w/ AudioNode
     // - mPlaybackRateTimeline - shared w/ AudioNode
     // - mDetuneTimeline - shared w/ AudioNode
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -107,16 +107,24 @@ public:
       NS_ASSERTION(mWriteIndex == mLength, "Overshot length");
       // Go to finished state. When the graph's current time eventually reaches
       // the end of the stream, then the main thread will be notified and we'll
       // shut down the AudioContext.
       *aFinished = true;
     }
   }
 
+  virtual bool IsActive() const override
+  {
+    // Keep processing to track stream time, which is used for all timelines
+    // associated with the same AudioContext.
+    return true;
+  }
+
+
   class OnCompleteTask final : public nsRunnable
   {
   public:
     OnCompleteTask(AudioContext* aAudioContext, AudioBuffer* aRenderedBuffer)
       : mAudioContext(aAudioContext)
       , mRenderedBuffer(aRenderedBuffer)
     {}
 
@@ -250,16 +258,26 @@ public:
 
       nsRefPtr<InputMutedRunnable> runnable =
         new InputMutedRunnable(aStream, newInputMuted);
       aStream->Graph()->
         DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
     }
   }
 
+  virtual bool IsActive() const override
+  {
+    // Keep processing to track stream time, which is used for all timelines
+    // associated with the same AudioContext.  If there are no other engines
+    // for the AudioContext, then this could return false to suspend the
+    // stream, but the stream is blocked anyway through
+    // AudioDestinationNode::SetIsOnlyNodeForContext().
+    return true;
+  }
+
   virtual void SetDoubleParameter(uint32_t aIndex, double aParam) override
   {
     if (aIndex == VOLUME) {
       mVolume = aParam;
     }
   }
 
   virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -304,18 +304,20 @@ public:
   }
 
   /**
    * Produce the next block of audio samples, given input samples aInput
    * (the mixed data for input 0).
    * aInput is guaranteed to have float sample format (if it has samples at all)
    * and to have been resampled to the sampling rate for the stream, and to have
    * exactly WEBAUDIO_BLOCK_SIZE samples.
-   * *aFinished is set to false by the caller. If the callee sets it to true,
-   * we'll finish the stream and not call this again.
+   * *aFinished is set to false by the caller. The callee must not set this to
+   * true unless silent output is produced. If set to true, we'll finish the
+   * stream, consider this input inactive on any downstream nodes, and not
+   * call this again.
    */
   virtual void ProcessBlock(AudioNodeStream* aStream,
                             const AudioBlock& aInput,
                             AudioBlock* aOutput,
                             bool* aFinished);
   /**
    * Produce the next block of audio samples, before input is provided.
    * ProcessBlock() will be called later, and it then should not change
@@ -341,16 +343,22 @@ public:
    * corresponding AudioNode, in which case it will be interpreted as a channel
    * of silence.
    */
   virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream,
                                     const OutputChunks& aInput,
                                     OutputChunks& aOutput,
                                     bool* aFinished);
 
+  // IsActive() returns true if the engine needs to continue processing an
+  // unfinished stream even when it has silent or no input connections.  This
+  // includes tail-times and when sources have been scheduled to start.  If
+  // returning false, then the stream can be suspended.
+  virtual bool IsActive() const { return false; }
+
   bool HasNode() const
   {
     MOZ_ASSERT(NS_IsMainThread());
     return !!mNode;
   }
 
   dom::AudioNode* NodeMainThread() const
   {
--- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp
@@ -104,18 +104,22 @@ static void ConvertSegmentToAudioBlock(A
       case AUDIO_FORMAT_S16: {
         CopyChunkToBlock<int16_t>(*ci, aBlock, duration);
         break;
       }
       case AUDIO_FORMAT_FLOAT32: {
         CopyChunkToBlock<float>(*ci, aBlock, duration);
         break;
       }
-      case AUDIO_FORMAT_SILENCE:
+      case AUDIO_FORMAT_SILENCE: {
+        // The actual type of the sample does not matter here, but we still need
+        // to send some audio to the graph.
+        CopyChunkToBlock<float>(*ci, aBlock, duration);
         break;
+      }
     }
     duration += ci->GetDuration();
   }
 }
 
 void
 AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
                                            uint32_t aFlags)
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -29,31 +29,33 @@ namespace mozilla {
 AudioNodeStream::AudioNodeStream(AudioNodeEngine* aEngine,
                                  Flags aFlags,
                                  TrackRate aSampleRate)
   : ProcessedMediaStream(nullptr),
     mEngine(aEngine),
     mSampleRate(aSampleRate),
     mFlags(aFlags),
     mNumberOfInputChannels(2),
+    mIsActive(aEngine->IsActive()),
     mMarkAsFinishedAfterThisBlock(false),
     mAudioParamStream(false),
     mPassThrough(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mChannelCountMode = ChannelCountMode::Max;
   mChannelInterpretation = ChannelInterpretation::Speakers;
   // AudioNodes are always producing data
   mHasCurrentData = true;
   mLastChunks.SetLength(std::max(uint16_t(1), mEngine->OutputCount()));
   MOZ_COUNT_CTOR(AudioNodeStream);
 }
 
 AudioNodeStream::~AudioNodeStream()
 {
+  MOZ_ASSERT(mActiveInputCount == 0);
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
 void
 AudioNodeStream::DestroyImpl()
 {
   // These are graph thread objects, so clean up on graph thread.
   mInputChunks.Clear();
@@ -515,17 +517,24 @@ AudioNodeStream::ProcessInput(GraphTime 
     EnsureTrack(AUDIO_TRACK);
   }
   // No more tracks will be coming
   mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   uint16_t outputCount = mLastChunks.Length();
   MOZ_ASSERT(outputCount == std::max(uint16_t(1), mEngine->OutputCount()));
 
-  if (mFinished || InMutedCycle()) {
+  if (!mIsActive) {
+    // mLastChunks are already null.
+#ifdef DEBUG
+    for (const auto& chunk : mLastChunks) {
+      MOZ_ASSERT(chunk.IsNull());
+    }
+#endif
+  } else if (InMutedCycle()) {
     mInputChunks.Clear();
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   } else {
     // We need to generate at least one input
     uint16_t maxInputs = std::max(uint16_t(1), mEngine->InputCount());
     mInputChunks.SetLength(maxInputs);
@@ -544,16 +553,17 @@ AudioNodeStream::ProcessInput(GraphTime 
       }
     }
     for (uint16_t i = 0; i < outputCount; ++i) {
       NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,
                    "Invalid WebAudio chunk size");
     }
     if (finished) {
       mMarkAsFinishedAfterThisBlock = true;
+      CheckForInactive();
     }
 
     if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
       for (uint32_t i = 0; i < outputCount; ++i) {
         mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
@@ -574,17 +584,17 @@ void
 AudioNodeStream::ProduceOutputBeforeInput(GraphTime aFrom)
 {
   MOZ_ASSERT(mEngine->AsDelayNodeEngine());
   MOZ_ASSERT(mEngine->OutputCount() == 1,
              "DelayNodeEngine output count should be 1");
   MOZ_ASSERT(!InMutedCycle(), "DelayNodes should break cycles");
   MOZ_ASSERT(mLastChunks.Length() == 1);
 
-  if (mFinished) {
+  if (!mIsActive) {
     mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
   } else {
     mEngine->ProduceBlockBeforeInput(&mLastChunks[0]);
     NS_ASSERTION(mLastChunks[0].GetDuration() == WEBAUDIO_BLOCK_SIZE,
                  "Invalid WebAudio chunk size");
     if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
       mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
@@ -679,9 +689,93 @@ AudioNodeStream::DestinationTimeFromTick
 {
   MOZ_ASSERT(SampleRate() == aDestination->SampleRate());
 
   GraphTime graphTime = StreamTimeToGraphTime(aPosition);
   StreamTime destinationTime = aDestination->GraphTimeToStreamTime(graphTime);
   return StreamTimeToSeconds(destinationTime);
 }
 
+void
+AudioNodeStream::AddInput(MediaInputPort* aPort)
+{
+  ProcessedMediaStream::AddInput(aPort);
+  AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
+  // Streams that are not AudioNodeStreams are considered active.
+  if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
+    IncrementActiveInputCount();
+  }
+}
+void
+AudioNodeStream::RemoveInput(MediaInputPort* aPort)
+{
+  ProcessedMediaStream::RemoveInput(aPort);
+  AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
+  // Streams that are not AudioNodeStreams are considered active.
+  if (!ns || (ns->mIsActive && !ns->IsAudioParamStream())) {
+    DecrementActiveInputCount();
+  }
+}
+
+void
+AudioNodeStream::SetActive()
+{
+  if (mIsActive || mMarkAsFinishedAfterThisBlock) {
+    return;
+  }
+
+  mIsActive = true;
+  if (IsAudioParamStream()) {
+    // Consumers merely influence stream order.
+    // They do not read from the stream.
+    return;
+  }
+
+  for (const auto& consumer : mConsumers) {
+    AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
+    if (ns) {
+      ns->IncrementActiveInputCount();
+    }
+  }
+}
+
+void
+AudioNodeStream::CheckForInactive()
+{
+  if (((mActiveInputCount > 0 || mEngine->IsActive()) &&
+       !mMarkAsFinishedAfterThisBlock) ||
+      !mIsActive) {
+    return;
+  }
+
+  mIsActive = false;
+  mInputChunks.Clear(); // not required for foreseeable future
+  for (auto& chunk : mLastChunks) {
+    chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
+  }
+  if (IsAudioParamStream()) {
+    return;
+  }
+
+  for (const auto& consumer : mConsumers) {
+    AudioNodeStream* ns = consumer->GetDestination()->AsAudioNodeStream();
+    if (ns) {
+      ns->DecrementActiveInputCount();
+    }
+  }
+}
+
+void
+AudioNodeStream::IncrementActiveInputCount()
+{
+  ++mActiveInputCount;
+  SetActive();
+}
+
+void
+AudioNodeStream::DecrementActiveInputCount()
+{
+  MOZ_ASSERT(mActiveInputCount > 0);
+  --mActiveInputCount;
+  CheckForInactive();
+}
+
 } // namespace mozilla
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -101,16 +101,18 @@ public:
 
   void SetAudioParamHelperStream()
   {
     MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
     mAudioParamStream = true;
   }
 
   virtual AudioNodeStream* AsAudioNodeStream() override { return this; }
+  virtual void AddInput(MediaInputPort* aPort) override;
+  virtual void RemoveInput(MediaInputPort* aPort) override;
 
   // Graph thread only
   void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                   double aStreamTime);
   void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
                                       ChannelCountMode aChannelCountMoe,
                                       ChannelInterpretation aChannelInterpretation);
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
@@ -160,48 +162,71 @@ public:
                                   StreamTime aPosition);
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
                                      AudioNodeSizes& aUsage) const;
 
+  /*
+   * SetActive() is called when either an active input is added or the engine
+   * for a source node transitions from inactive to active.  This is not
+   * called from engines for processing nodes because they only become active
+   * when there are active input streams, in which case this stream is already
+   * active.
+   */
+  void SetActive();
+  /*
+   * CheckForInactive() is called when the engine transitions from active to
+   * inactive, or an active input is removed, or the stream finishes.  If the
+   * stream is now inactive, then mInputChunks will be cleared and mLastChunks
+   * will be set to null.  ProcessBlock() will not be called on the engine
+   * again until SetActive() is called.
+   */
+  void CheckForInactive();
 
 protected:
   virtual void DestroyImpl() override;
 
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk,
                             AudioBlock* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioBlock* aChunk, uint32_t aOutputChannelCount,
                          nsTArray<const float*>& aOutputChannels,
                          nsTArray<float>& aDownmixBuffer);
 
   uint32_t ComputedNumberOfChannels(uint32_t aInputChannelCount);
   void ObtainInputBlock(AudioBlock& aTmpChunk, uint32_t aPortIndex);
+  void IncrementActiveInputCount();
+  void DecrementActiveInputCount();
 
   // The engine that will generate output for this node.
   nsAutoPtr<AudioNodeEngine> mEngine;
   // The mixed input blocks are kept from iteration to iteration to avoid
   // reallocating channel data arrays and any buffers for mixing.
   OutputChunks mInputChunks;
   // The last block produced by this node.
   OutputChunks mLastChunks;
   // The stream's sampling rate
   const TrackRate mSampleRate;
   // Whether this is an internal or external stream
   const Flags mFlags;
+  // The number of input streams that may provide non-silent input.
+  uint32_t mActiveInputCount = 0;
   // The number of input channels that this stream requires. 0 means don't care.
   uint32_t mNumberOfInputChannels;
   // The mixing modes
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
+  // Streams are considered active if the stream has not finished and either
+  // the engine is active or there are active input streams.
+  bool mIsActive;
   // Whether the stream should be marked as finished as soon
   // as the current time range has been computed block by block.
   bool mMarkAsFinishedAfterThisBlock;
   // Whether the stream is an AudioParamHelper stream.
   bool mAudioParamStream;
   // Whether the stream just passes its input through.
   bool mPassThrough;
 };
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -150,16 +150,17 @@ public:
         if (mBiquads[i].hasTail()) {
           hasTail = true;
           break;
         }
       }
       if (!hasTail) {
         if (!mBiquads.IsEmpty()) {
           mBiquads.Clear();
+          aStream->CheckForInactive();
 
           nsRefPtr<PlayingRefChangeHandler> refchanged =
             new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
           aStream->Graph()->
             DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
         }
 
         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
@@ -206,16 +207,21 @@ public:
       SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
 
       mBiquads[i].process(input,
                           aOutput->ChannelFloatsForWrite(i),
                           aInput.GetDuration());
     }
   }
 
+  virtual bool IsActive() const override
+  {
+    return !mBiquads.IsEmpty();
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     // Not owned:
     // - mSource - probably not owned
     // - mDestination - probably not owned
     // - AudioParamTimelines - counted in the AudioNode
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     amount += mBiquads.ShallowSizeOfExcludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/BufferDecoder.cpp
+++ b/dom/media/webaudio/BufferDecoder.cpp
@@ -41,23 +41,16 @@ BufferDecoder::BeginDecoding(TaskQueue* 
 
 ReentrantMonitor&
 BufferDecoder::GetReentrantMonitor()
 {
   return mReentrantMonitor;
 }
 
 bool
-BufferDecoder::IsShutdown() const
-{
-  // BufferDecoder cannot be shut down.
-  return false;
-}
-
-bool
 BufferDecoder::OnStateMachineTaskQueue() const
 {
   // BufferDecoder doesn't have the concept of a state machine.
   return true;
 }
 
 bool
 BufferDecoder::OnDecodeTaskQueue() const
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -28,18 +28,16 @@ public:
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // This has to be called before decoding begins
   void BeginDecoding(TaskQueue* aTaskQueueIdentity);
 
   virtual ReentrantMonitor& GetReentrantMonitor() final override;
 
-  virtual bool IsShutdown() const final override;
-
   virtual bool OnStateMachineTaskQueue() const final override;
 
   virtual bool OnDecodeTaskQueue() const final override;
 
   virtual MediaResource* GetResource() const final override;
 
   virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override;
 
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -115,16 +115,17 @@ public:
     if (aInput.IsNull()) {
       if (mLeftOverData > 0) {
         mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
         input.AllocateChannels(1);
         WriteZeroesToAudioBlock(&input, 0, WEBAUDIO_BLOCK_SIZE);
       } else {
         if (mLeftOverData != INT32_MIN) {
           mLeftOverData = INT32_MIN;
+          aStream->CheckForInactive();
           nsRefPtr<PlayingRefChanged> refchanged =
             new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
           aStream->Graph()->
             DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
         }
         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
         return;
       }
@@ -149,16 +150,21 @@ public:
       mLeftOverData = mBufferLength;
       MOZ_ASSERT(mLeftOverData > 0);
     }
     aOutput->AllocateChannels(2);
 
     mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE);
   }
 
+  virtual bool IsActive() const override
+  {
+    return mLeftOverData != INT32_MIN;
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     if (mBuffer && !mBuffer->IsShared()) {
       amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
     }
 
     if (mReverb) {
--- a/dom/media/webaudio/DelayNode.cpp
+++ b/dom/media/webaudio/DelayNode.cpp
@@ -91,25 +91,27 @@ public:
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
       mLeftOverData = mBuffer.MaxDelayTicks();
     } else if (mLeftOverData > 0) {
       mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
     } else {
       if (mLeftOverData != INT32_MIN) {
         mLeftOverData = INT32_MIN;
+        aStream->CheckForInactive();
+
         // Delete our buffered data now we no longer need it
         mBuffer.Reset();
 
         nsRefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
         aStream->Graph()->
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
-      *aOutput = aInput;
+      aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
     mBuffer.Write(aInput);
 
     // Skip output update if mLastChunks has already been set by
     // ProduceBlockBeforeInput() when in a cycle.
     if (!mHaveProducedBeforeInput) {
@@ -156,16 +158,21 @@ public:
     if (mLeftOverData <= 0) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     } else {
       UpdateOutputBlock(aOutput, WEBAUDIO_BLOCK_SIZE);
     }
     mHaveProducedBeforeInput = true;
   }
 
+  virtual bool IsActive() const override
+  {
+    return mLeftOverData != INT32_MIN;
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     // Not owned:
     // - mSource - probably not owned
     // - mDestination - probably not owned
     // - mDelay - shares ref with AudioNode, don't count
     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -76,17 +76,20 @@ public:
     default:
       NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
     }
   }
 
   virtual void SetStreamTimeParameter(uint32_t aIndex, StreamTime aParam) override
   {
     switch (aIndex) {
-    case START: mStart = aParam; break;
+    case START:
+      mStart = aParam;
+      mSource->SetActive();
+      break;
     case STOP: mStop = aParam; break;
     default:
       NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
     }
   }
 
   virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
   {
@@ -325,16 +328,22 @@ public:
         ComputeCustom(output, ticks, start, end);
         break;
       default:
         ComputeSilence(aOutput);
     };
 
   }
 
+  virtual bool IsActive() const override
+  {
+    // start() has been called.
+    return mStart != -1;
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
 
     // Not owned:
     // - mSource
     // - mDestination
     // - mFrequency (internal ref owned by node)
--- a/dom/media/webaudio/PannerNode.cpp
+++ b/dom/media/webaudio/PannerNode.cpp
@@ -144,39 +144,45 @@ public:
       // tail-time reference was added.  Even if the model is now equalpower,
       // the reference will need to be removed.
       if (mLeftOverData > 0 &&
           mPanningModelFunction == &PannerNodeEngine::HRTFPanningFunction) {
         mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
       } else {
         if (mLeftOverData != INT_MIN) {
           mLeftOverData = INT_MIN;
+          aStream->CheckForInactive();
           mHRTFPanner->reset();
 
           nsRefPtr<PlayingRefChangeHandler> refchanged =
             new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
           aStream->Graph()->
             DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
         }
-        *aOutput = aInput;
+        aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
         return;
       }
     } else if (mPanningModelFunction == &PannerNodeEngine::HRTFPanningFunction) {
       if (mLeftOverData == INT_MIN) {
         nsRefPtr<PlayingRefChangeHandler> refchanged =
           new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
         aStream->Graph()->
           DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
       }
       mLeftOverData = mHRTFPanner->maxTailFrames();
     }
 
     (this->*mPanningModelFunction)(aInput, aOutput);
   }
 
+  virtual bool IsActive() const override
+  {
+    return mLeftOverData != INT_MIN;
+  }
+
   void ComputeAzimuthAndElevation(float& aAzimuth, float& aElevation);
   float ComputeConeGain();
   // Compute how much the distance contributes to the gain reduction.
   float ComputeDistanceGain();
 
   void EqualPowerPanningFunction(const AudioBlock& aInput, AudioBlock* aOutput);
   void HRTFPanningFunction(const AudioBlock& aInput, AudioBlock* aOutput);
 
--- a/dom/media/webaudio/ScriptProcessorNode.cpp
+++ b/dom/media/webaudio/ScriptProcessorNode.cpp
@@ -328,16 +328,24 @@ public:
     *aOutput = mSharedBuffers->GetOutputBuffer();
 
     if (mInputWriteIndex >= mBufferSize) {
       SendBuffersToMainThread(aStream);
       mInputWriteIndex -= mBufferSize;
     }
   }
 
+  virtual bool IsActive() const override
+  {
+    // Could return false when !mIsConnected after all output chunks produced
+    // by main thread events calling
+    // SharedBuffers::FinishProducingOutputBuffer() have been processed.
+    return true;
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     // Not owned:
     // - mSource (probably)
     // - mDestination (probably)
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     amount += mSharedBuffers->SizeOfIncludingThis(aMallocSizeOf);
     amount += mInputBuffer->SizeOfIncludingThis(aMallocSizeOf);
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -116,16 +116,18 @@ skip-if = toolkit == 'android' # bug 105
 [test_dynamicsCompressorNodePassThrough.html]
 [test_gainNode.html]
 [test_gainNodeInLoop.html]
 [test_gainNodePassThrough.html]
 [test_maxChannelCount.html]
 [test_mediaDecoding.html]
 [test_mediaElementAudioSourceNode.html]
 tags=capturestream
+[test_mediaElementAudioSourceNodeFidelity.html]
+tags=capturestream
 [test_mediaElementAudioSourceNodePassThrough.html]
 tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaElementAudioSourceNodeCrossOrigin.html]
 tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaStreamAudioDestinationNode.html]
 [test_mediaStreamAudioSourceNode.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeFidelity.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaStreamAudioSourceNode doesn't get data from cross-origin media resources</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+// Get an Opus file containing a sine wave at maximum amplitude, of duration
+// `lengthSeconds`, and of frequency `frequency`.
+function getSineWaveFile(frequency, lengthSeconds, callback) {
+  var chunks = [];
+  var off = new OfflineAudioContext(1, lengthSeconds * 48000, 48000);
+  var osc = off.createOscillator();
+  var rec = new MediaRecorder(osc);
+  rec.ondataavailable = function(e) {
+    chunks.push(e.data);
+  };
+  rec.onstop = function(e) {
+    var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
+    callback(blob);
+  }
+  osc.frequency.value = 1.0;
+  osc.start();
+  rec.start();
+  off.startRendering().then(function(buffer) {
+    rec.stop();
+  });
+}
+
+function binIndexForFrequency(frequency, analyser) {
+  return 1 + Math.round(frequency *
+                        analyser.fftSize /
+                        analyser.context.sampleRate);
+}
+
+function debugCanvas(analyser) {
+  var cvs = document.createElement("canvas");
+  document.body.appendChild(cvs);
+
+  // Easy: 1px per bin
+  cvs.width = analyser.frequencyBinCount;
+  cvs.height = 256;
+  cvs.style.border = "1px solid red";
+
+  var c = cvs.getContext('2d');
+  var buf = new Uint8Array(analyser.frequencyBinCount);
+
+  function render() {
+    c.clearRect(0, 0, cvs.width, cvs.height);
+    analyser.getByteFrequencyData(buf);
+    for (var i = 0; i < buf.length; i++) {
+      c.fillRect(i, (256 - (buf[i])), 1, 256);
+    }
+    requestAnimationFrame(render);
+  }
+  requestAnimationFrame(render);
+}
+
+
+function checkFrequency(an) {
+  an.getFloatFrequencyData(frequencyArray);
+  // We should have no energy when checking the data largely outside the index
+  // for 440Hz (the frequency of the sine wave), start checking an octave above,
+  // the Opus compression can add some harmonics to the pure since wave.
+  var index = binIndexForFrequency(880, an);
+  var underTreshold = true;
+  for (var i = index; i < frequencyArray.length; i++) {
+    // Let some slack, there might be some noise here because of int -> float
+    // conversion or the Opus encoding.
+    if (frequencyArray[i] > an.minDecibels + 40) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function test() {
+  getSineWaveFile(440, 1, (blob) => {
+      var audioElement = new Audio();
+      audioElement.src = URL.createObjectURL(blob);
+      audioElement.loop = true;
+      var ac = new AudioContext();
+      var mediaElementSource = ac.createMediaElementSource(audioElement);
+      var an = ac.createAnalyser();
+      frequencyArray = new Float32Array(an.frequencyBinCount);
+
+      // Uncomment this to check what the analyser is doing.
+      // debugCanvas(an);
+
+      mediaElementSource.connect(an);
+
+      audioElement.play();
+      // We want to check the we have the expected audio for at least one loop of
+      // the HTMLMediaElement. The file is one second, and we use the default FFT
+      // size.
+      var lastCurrentTime = 0;
+      var looped = false;
+      audioElement.onplaying = function() {
+        audioElement.ontimeupdate = function() {
+          if (checkFrequency(an)) {
+            ok(true, "Found correct audio signal during analysis");
+            dump(lastCurrentTime + " " + audioElement.currentTime + "\n");
+            if (lastCurrentTime > audioElement.currentTime) {
+              if (looped) {
+                audioElement.ontimeupdate = null;
+                audioElement.onplaying = null;
+                SimpleTest.finish()
+              }
+              lastCurrentTime = audioElement.currentTime;
+              looped = true;
+            } else {
+              lastCurrentTime = audioElement.currentTime;
+            }
+          } else {
+            ok(false, "Found unexpected noise during analysis.");
+            audioElement.ontimeupdate = null;
+            audioElement.onplaying = null;
+            ac.close();
+            audioElement.src = '';
+            SimpleTest.finish()
+          }
+        }
+      }
+  });
+}
+
+SpecialPowers.pushPrefEnv(
+    {'set': [['media.recorder.audio_node.enabled', true]]}, test);
+
+</script>
--- a/dom/presentation/tests/mochitest/test_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver.html
@@ -128,17 +128,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
@@ -166,17 +166,16 @@ function runTests() {
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
   {type: 'browser', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0],
                                       ["dom.mozBrowserFramesEnabled", true],
                                       ["dom.ipc.browser_frames.oop_by_default", true]]},
                             runTests);
 });
 
 </script>
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_start_session_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver_start_session_error.html
@@ -94,17 +94,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_start_session_timeout.html
+++ b/dom/presentation/tests/mochitest/test_presentation_receiver_start_session_timeout.html
@@ -67,17 +67,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0],
                                       ["presentation.receiver.loading.timeout", 10]]},
                             runTests);
 });
 
 </script>
 </body>
--- a/dom/presentation/tests/mochitest/test_presentation_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender.html
@@ -188,17 +188,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_sender_default_request.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_default_request.html
@@ -134,17 +134,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
@@ -155,17 +155,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_start_session_error.html
@@ -247,17 +247,16 @@ function runTests() {
 
 SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.ignore_webidl_scope_checks", true],
                                       ["dom.presentation.test.enabled", true],
                                       ["dom.presentation.test.stage", 0]]},
                             runTests);
 });
 
 </script>
 </body>
 </html>
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -110,16 +110,24 @@ nsCSPContext::ShouldLoad(nsContentPolicy
                          int16_t*            outDecision)
 {
   if (CSPCONTEXTLOGENABLED()) {
     nsAutoCString spec;
     aContentLocation->GetSpec(spec);
     CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s", spec.get()));
   }
 
+  bool isStyleOrScriptPreLoad =
+    (aContentType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
+     aContentType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD);
+
+  // Since we know whether we are dealing with a preload, we have to convert
+  // the internal policytype ot the external policy type before moving on.
+  aContentType = nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
+
   nsresult rv = NS_OK;
 
   // This ShouldLoad function is called from nsCSPService::ShouldLoad,
   // which already checked a number of things, including:
   // * aContentLocation is not null; we can consume this without further checks
   // * scheme is not a whitelisted scheme (about: chrome:, etc).
   // * CSP is enabled
   // * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
@@ -140,65 +148,44 @@ nsCSPContext::ShouldLoad(nsContentPolicy
 
   // If the content type doesn't map to a CSP directive, there's nothing for
   // CSP to do.
   CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
   if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
     return NS_OK;
   }
 
-  // This may be a load or a preload. If it is a preload, the document will
-  // not have been fully parsed yet, and aRequestContext will be an
-  // nsIDOMHTMLDocument rather than the nsIDOMHTMLElement associated with the
-  // resource. As a result, we cannot extract the element's corresponding
-  // nonce attribute, and so we cannot correctly check the nonce on a preload.
-  //
-  // Therefore, the decision returned here for a preload may be *incorrect* as
-  // it cannot take the nonce into account. We will still check the load, but
-  // we will not cache the result or report a violation. When the "real load"
-  // happens subsequently, we will re-check with the additional context to
-  // make a final decision.
-  //
-  // We don't just return false because that would block all preloads and
-  // degrade performance. However, we do want to block preloads that are
-  // clearly blocked (their urls are not whitelisted) by CSP.
-
-  nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryInterface(aRequestContext);
-  bool isPreload = doc &&
-                   (aContentType == nsIContentPolicy::TYPE_SCRIPT ||
-                    aContentType == nsIContentPolicy::TYPE_STYLESHEET);
-
   nsAutoString nonce;
-  if (!isPreload) {
+  if (!isStyleOrScriptPreLoad) {
     nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(aRequestContext);
     if (htmlElement) {
       rv = htmlElement->GetAttribute(NS_LITERAL_STRING("nonce"), nonce);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // aExtra is only non-null if the channel got redirected.
   bool wasRedirected = (aExtra != nullptr);
   nsCOMPtr<nsIURI> originalURI = do_QueryInterface(aExtra);
 
   bool permitted = permitsInternal(dir,
                                    aContentLocation,
                                    originalURI,
                                    nonce,
                                    wasRedirected,
-                                   isPreload,
+                                   isStyleOrScriptPreLoad,
                                    false,     // allow fallback to default-src
                                    true,      // send violation reports
                                    true);     // send blocked URI in violation reports
 
   *outDecision = permitted ? nsIContentPolicy::ACCEPT
                            : nsIContentPolicy::REJECT_SERVER;
 
   // Done looping, cache any relevant result
-  if (cacheKey.Length() > 0 && !isPreload) {
+  if (cacheKey.Length() > 0 && !isStyleOrScriptPreLoad) {
     mShouldLoadCache.Put(cacheKey, *outDecision);
   }
 
   if (CSPCONTEXTLOGENABLED()) {
     nsAutoCString spec;
     aContentLocation->GetSpec(spec);
     CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
   }
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -100,18 +100,19 @@ CSPService::ShouldLoad(uint32_t aContent
                        nsIURI *aContentLocation,
                        nsIURI *aRequestOrigin,
                        nsISupports *aRequestContext,
                        const nsACString &aMimeTypeGuess,
                        nsISupports *aExtra,
                        nsIPrincipal *aRequestPrincipal,
                        int16_t *aDecision)
 {
-  MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
-             "We should only see external content policy types here.");
+  MOZ_ASSERT(aContentType ==
+             nsContentUtils::InternalContentPolicyTypeToExternalOrPreload(aContentType),
+             "We should only see external content policy types or preloads here.");
 
   if (!aContentLocation) {
     return NS_ERROR_FAILURE;
   }
 
   if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
     nsAutoCString location;
     aContentLocation->GetSpec(location);
@@ -246,18 +247,19 @@ CSPService::ShouldProcess(uint32_t      
                           nsIURI           *aContentLocation,
                           nsIURI           *aRequestOrigin,
                           nsISupports      *aRequestContext,
                           const nsACString &aMimeTypeGuess,
                           nsISupports      *aExtra,
                           nsIPrincipal     *aRequestPrincipal,
                           int16_t          *aDecision)
 {
-  MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
-             "We should only see external content policy types here.");
+  MOZ_ASSERT(aContentType ==
+             nsContentUtils::InternalContentPolicyTypeToExternalOrPreload(aContentType),
+             "We should only see external content policy types or preloads here.");
 
   if (!aContentLocation)
     return NS_ERROR_FAILURE;
 
   *aDecision = nsIContentPolicy::ACCEPT;
   return NS_OK;
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import.css
@@ -0,0 +1,1 @@
+@import url("http://example.com/tests/dom/security/test/csp/file_report_for_import_server.sjs?stylesheet");
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Bug 1048048 - Test sending csp-report when using import in css</title>
+    <link rel="stylesheet" type="text/css" href="file_report_for_import.css">
+  </head>
+  <body>
+  empty body, just testing @import in the included css for bug 1048048
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import_server.sjs
@@ -0,0 +1,49 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 1048048 - CSP violation report not sent for @import
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+
+function handleRequest(request, response)
+{
+  // avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setHeader("Content-Type", "text/html", false);
+  var queryString = request.queryString;
+
+  // (1) lets process the queryresult request async and
+  // wait till we have received the image request.
+  if (queryString === "queryresult") {
+    response.processAsync();
+    setObjectState("queryResult", response);
+    return;
+  }
+
+  // (2) handle the csp-report and return the JSON back to
+  // the testfile using the afore stored xml request in (1).
+  if (queryString === "report") {
+    getObjectState("queryResult", function(queryResponse) {
+      if (!queryResponse) {
+        return;
+      }
+
+      // send the report back to the XML request for verification
+      var report = new BinaryInputStream(request.bodyInputStream);
+      var avail;
+      var bytes = [];
+      while ((avail = report.available()) > 0) {
+        Array.prototype.push.apply(bytes, report.readByteArray(avail));
+      }
+      var data = String.fromCharCode.apply(null, bytes);
+      queryResponse.bodyOutputStream.write(data, data.length);
+      queryResponse.finish();
+    });
+    return;
+  }
+
+  // we should not get here ever, but just in case return
+  // something unexpected.
+  response.write("doh!");
+}
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -122,16 +122,19 @@ support-files =
   file_upgrade_insecure_server.sjs
   file_upgrade_insecure_wsh.py
   file_upgrade_insecure_reporting.html
   file_upgrade_insecure_reporting_server.sjs
   file_upgrade_insecure_referrer.html
   file_upgrade_insecure_referrer_server.sjs
   file_upgrade_insecure_cors.html
   file_upgrade_insecure_cors_server.sjs
+  file_report_for_import.css
+  file_report_for_import.html
+  file_report_for_import_server.sjs
 
 [test_base-uri.html]
 [test_blob_data_schemes.html]
 [test_connect-src.html]
 [test_CSP.html]
 [test_allow_https_schemes.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_bug663567.html]
@@ -186,10 +189,11 @@ skip-if = buildapp == 'b2g' #no ssl supp
 # no ssl support as well as websocket tests do not work (see test_websocket.html)
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_reporting.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_referrer.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
 [test_upgrade_insecure_cors.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
+[test_report_for_import.html]
 [test_blocked_uri_in_reports.html]
 skip-if = e10s || buildapp == 'b2g' # http-on-opening-request observer not supported in child process (bug 1009632)
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_report_for_import.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=548193
+-->
+<head>
+  <title>Test for Bug 548193</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We are loading a stylesheet using a csp policy that only allows styles from 'self'
+ * to be loaded. In other words, the *.css file itself should be allowed to load, but
+ * the @import file within the CSS should get blocked. We verify that the generated
+ * csp-report is sent and contains all the expected values.
+ * In detail, the test starts by sending an XHR request to the report-server
+ * which waits on the server side till the report was received and hands the
+ * report in JSON format back to the testfile which then verifies accuracy
+ * of all the different report fields in the CSP report.
+ */
+
+const TEST_FILE = "tests/dom/security/test/csp/file_report_for_import.html";
+const REPORT_URI =
+  "http://mochi.test:8888/tests/dom/security/test/csp/file_report_for_import_server.sjs?report";
+const POLICY = "style-src 'self'; report-uri " + REPORT_URI;
+
+const DOC_URI =
+  "http://mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs?" +
+  "file=tests/dom/security/test/csp/file_report_for_import.html&" +
+  "csp=style-src%20%27self%27%3B%20" +
+  "report-uri%20http%3A//mochi.test%3A8888/tests/dom/security/test/csp/" +
+  "file_report_for_import_server.sjs%3Freport";
+
+function checkResults(reportStr) {
+  try {
+    var reportObj = JSON.parse(reportStr);
+    var cspReport = reportObj["csp-report"];
+
+    is(cspReport["document-uri"], DOC_URI, "Incorrect document-uri");
+    is(cspReport["referrer"],
+       "http://mochi.test:8888/tests/dom/security/test/csp/test_report_for_import.html",
+       "Incorrect referrer");
+    is(cspReport["violated-directive"],
+       "style-src http://mochi.test:8888",
+       "Incorrect violated-directive");
+    is(cspReport["original-policy"],
+       "style-src http://mochi.test:8888; report-uri " +
+       "http://mochi.test:8888/tests/dom/security/test/csp/file_report_for_import_server.sjs?report",
+       "Incorrect original-policy");
+    is(cspReport["blocked-uri"],
+       "http://example.com/tests/dom/security/test/csp/file_report_for_import_server.sjs?stylesheet",
+       "Incorrect blocked-uri");
+
+    // we do not always set the following fields
+    is(cspReport["source-file"], undefined, "Incorrect source-file");
+    is(cspReport["script-sample"], undefined, "Incorrect script-sample");
+    is(cspReport["line-number"], undefined, "Incorrect line-number");
+  }
+  catch (e) {
+    ok(false, "Could not parse JSON (exception: " + e + ")");
+  }
+}
+
+function loadTestPageIntoFrame() {
+  // load the resource which will generate a CSP violation report
+  // save this for last so that our listeners are registered.
+  var src = "file_testserver.sjs";
+  // append the file that should be served
+  src += "?file=" + escape(TEST_FILE);
+  // append the CSP that should be used to serve the file
+  src += "&csp=" + escape(POLICY);
+  // appending a fragment so we can test that it's correctly stripped
+  // for document-uri and source-file.
+  src += "#foo";
+  document.getElementById("cspframe").src = src;
+}
+
+function runTest() {
+  // send an xhr request to the server which is processed async, which only
+  // returns after the server has received the csp report.
+  var myXHR = new XMLHttpRequest();
+  myXHR.open("GET", "file_report_for_import_server.sjs?queryresult");
+  myXHR.onload = function(e) {
+    checkResults(myXHR.responseText);
+    SimpleTest.finish();
+  }
+  myXHR.onerror = function(e) {
+    ok(false, "could not query results from server (" + e.message + ")");
+    SimpleTest.finish();
+  }
+  myXHR.send();
+
+  // give it some time and run the testpage
+  SimpleTest.executeSoon(loadTestPageIntoFrame);
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -72,17 +72,19 @@ static const uint32_t sMaxStreamVolumeTb
   15,  // ring
   15,  // music
   15,  // alarm
   15,  // notification
   15,  // BT SCO
   15,  // enforced audible
   15,  // DTMF
   15,  // TTS
+#if ANDROID_VERSION < 19
   15,  // FM
+#endif
 };
 
 // Use a half value of each volume category as the default volume.
 static const uint32_t sDefaultVolumeCategoriesTbl[VOLUME_TOTAL_NUMBER] = {
   8, // VOLUME_MEDIA
   8, // VOLUME_NOTIFICATION
   8, // VOLUME_ALARM
   3, // VOLUME_TELEPHONY
@@ -789,22 +791,26 @@ AudioManager::GetFmRadioAudioEnabled(boo
 
 NS_IMETHODIMP
 AudioManager::SetFmRadioAudioEnabled(bool aFmRadioAudioEnabled)
 {
   AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_FM,
     aFmRadioAudioEnabled ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
     AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, "");
   UpdateHeadsetConnectionState(GetCurrentSwitchState(SWITCH_HEADPHONES));
+  // AUDIO_STREAM_FM is not used on recent gonk.
+  // AUDIO_STREAM_MUSIC is used for FM radio volume control.
+#if ANDROID_VERSION < 19
   // sync volume with music after powering on fm radio
   if (aFmRadioAudioEnabled) {
     uint32_t volIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
     SetStreamVolumeIndex(AUDIO_STREAM_FM, volIndex);
     mCurrentStreamVolumeTbl[AUDIO_STREAM_FM] = volIndex;
   }
+#endif
   return NS_OK;
 }
 
 nsresult
 AudioManager::ValidateVolumeIndex(uint32_t aCategory, uint32_t aIndex) const
 {
   uint32_t maxIndex = GetMaxVolumeByCategory(aCategory);
   if (aIndex > maxIndex) {
@@ -814,23 +820,27 @@ AudioManager::ValidateVolumeIndex(uint32
 }
 
 nsresult
 AudioManager::SetVolumeByCategory(uint32_t aCategory, uint32_t aIndex)
 {
   nsresult status;
   switch (static_cast<AudioVolumeCategories>(aCategory)) {
     case VOLUME_MEDIA:
+      // AUDIO_STREAM_FM is not used on recent gonk.
+      // AUDIO_STREAM_MUSIC is used for FM radio volume control.
+#if ANDROID_VERSION < 19
       // sync FMRadio's volume with content channel.
       if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) {
         status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex);
         if (NS_WARN_IF(NS_FAILED(status))) {
           return status;
         }
       }
+#endif
       status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex);
       break;
     case VOLUME_NOTIFICATION:
       status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
       if (NS_WARN_IF(NS_FAILED(status))) {
         return status;
       }
       status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
@@ -1000,24 +1010,25 @@ AudioManager::SetStreamVolumeIndex(int32
   mCurrentStreamVolumeTbl[aStream] = aIndex;
   status_t status;
 #if ANDROID_VERSION < 17
    status = AudioSystem::setStreamVolumeIndex(
               static_cast<audio_stream_type_t>(aStream),
               aIndex);
    return status ? NS_ERROR_FAILURE : NS_OK;
 #else
+#if ANDROID_VERSION < 19
   if (aStream == AUDIO_STREAM_FM) {
     status = AudioSystem::setStreamVolumeIndex(
                static_cast<audio_stream_type_t>(aStream),
                aIndex,
                AUDIO_DEVICE_OUT_FM);
     return status ? NS_ERROR_FAILURE : NS_OK;
   }
-
+#endif
   if (mPresentProfile == DEVICE_PRIMARY) {
     status = AudioSystem::setStreamVolumeIndex(
             static_cast<audio_stream_type_t>(aStream),
             aIndex,
             AUDIO_DEVICE_OUT_SPEAKER);
     status += AudioSystem::setStreamVolumeIndex(
             static_cast<audio_stream_type_t>(aStream),
             aIndex,
--- a/dom/system/gonk/android_audio/AudioSystem.h
+++ b/dom/system/gonk/android_audio/AudioSystem.h
@@ -69,17 +69,19 @@ typedef enum {
     AUDIO_STREAM_RING             = 2,
     AUDIO_STREAM_MUSIC            = 3,
     AUDIO_STREAM_ALARM            = 4,
     AUDIO_STREAM_NOTIFICATION     = 5,
     AUDIO_STREAM_BLUETOOTH_SCO    = 6,
     AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */
     AUDIO_STREAM_DTMF             = 8,
     AUDIO_STREAM_TTS              = 9,
+#if ANDROID_VERSION < 19
     AUDIO_STREAM_FM               = 10,
+#endif
 
     AUDIO_STREAM_CNT,
     AUDIO_STREAM_MAX              = AUDIO_STREAM_CNT - 1,
 } audio_stream_type_t;
 
 /* PCM sub formats */
 typedef enum {
     AUDIO_FORMAT_PCM_SUB_16_BIT          = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */
--- a/dom/tests/mochitest/general/frameStorageChrome.html
+++ b/dom/tests/mochitest/general/frameStorageChrome.html
@@ -2,17 +2,18 @@
 <head>
 <title>frame for storage allowed test</title>
 
 <script type="text/javascript" src="https://example.com/tests/dom/tests/mochitest/general/storagePermissionsUtils.js"></script>
 <script type="text/javascript">
 
   task(function* () {
     // We just check if we can access storage as chrome using special powers!
-    yield chromePower();
+    var params = new URLSearchParams(location.search.substr(1));
+    yield chromePower(params.get('allowed') == 'yes', params.get('blockSessionStorage') == 'yes');
   });
 
 </script>
 
 </head>
 
 <body>
 </body>
--- a/dom/tests/mochitest/general/storagePermissionsUtils.js
+++ b/dom/tests/mochitest/general/storagePermissionsUtils.js
@@ -57,54 +57,59 @@ function runWorker(url) {
         return;
       }
 
       ok(!e.data.match(/^FAILURE/), e.data + " (WORKER = " + url + ")");
     });
   });
 }
 
-function chromePower() {
+function chromePower(allowed, blockSessionStorage) {
+
+  // localStorage is affected by storage policy.
   try {
     SpecialPowers.wrap(window).localStorage.getItem("X");
-    ok(true, "getting localStorage didn't throw");
+    ok(allowed, "getting localStorage from chrome didn't throw");
   } catch (e) {
-    ok(false, "getting localStorage should not throw");
+    ok(!allowed, "getting localStorage from chrome threw");
   }
 
+
+  // sessionStorage is not. See bug 1183968.
   try {
     SpecialPowers.wrap(window).sessionStorage.getItem("X");
-    ok(true, "getting sessionStorage didn't throw");
+    ok(!blockSessionStorage, "getting sessionStorage from chrome didn't throw");
   } catch (e) {
-    ok(false, "getting sessionStorage should not throw");
+    ok(blockSessionStorage, "getting sessionStorage from chrome threw");
   }
 
+  // indexedDB is affected by storage policy.
   try {
     SpecialPowers.wrap(window).indexedDB;
-    ok(true, "getting indexedDB didn't throw");
+    ok(allowed, "getting indexedDB from chrome didn't throw");
   } catch (e) {
-    ok(false, "getting indexedDB should not throw");
+    ok(!allowed, "getting indexedDB from chrome threw");
   }
 
+  // Same with caches, along with the additional https-only requirement.
   try {
+    var shouldResolve = allowed && location.protocol == "https:";
     var promise = SpecialPowers.wrap(window).caches.keys();
-    ok(true, "getting caches didn't throw");
-
+    ok(true, "getting caches from chrome should never throw");
     return new Promise((resolve, reject) => {
       promise.then(function() {
-        ok(location.protocol == "https:", "The promise was not rejected");
+        ok(shouldResolve, "The promise was resolved for chrome");
         resolve();
       }, function(e) {
-        ok(location.protocol != "https:", "The promise should not have been rejected: " + e);
+        ok(!shouldResolve, "The promise was rejected for chrome: " + e);
         resolve();
       });
     });
   } catch (e) {
-    ok(false, "getting caches should not have thrown");
-    return Promise.resolve();
+    ok(false, "getting caches from chrome threw");
   }
 }
 
 function storageAllowed() {
   try {
     localStorage.getItem("X");
     ok(true, "getting localStorage didn't throw");
   } catch (e) {
--- a/dom/tests/mochitest/general/test_storagePermissionsAccept.html
+++ b/dom/tests/mochitest/general/test_storagePermissionsAccept.html
@@ -15,28 +15,28 @@ task(function* () {
   yield setCookieBehavior(BEHAVIOR_ACCEPT);
 
   // We should be able to access storage
   yield storageAllowed();
 
   // Same origin iframes should be allowed, unless they redirect to a URI with the null principal
   yield runIFrame("frameStorageAllowed.html");
   yield runIFrame("frameStorageNullprincipal.sjs");
-  yield runIFrame("frameStorageChrome.html");
+  yield runIFrame("frameStorageChrome.html?allowed=yes");
 
   // Sandboxed iframes should have the null principal, and thus can't access storage
   document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts');
   yield runIFrame("frameStoragePrevented.html#nullprincipal");
   yield runIFrame("frameStorageNullprincipal.sjs");
   document.querySelector('iframe').removeAttribute('sandbox');
 
   // Thirdparty iframes should be allowed, unless they redirect to a URI with the null principal
   yield runIFrame(thirdparty + "frameStorageAllowed.html");
   yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs");
-  yield runIFrame(thirdparty + "frameStorageChrome.html");
+  yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=yes");
 
   // Workers should be able to access storage
   yield runWorker("workerStorageAllowed.js");
 });
 
     </script>
   </body>
 </html>
--- a/dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html
+++ b/dom/tests/mochitest/general/test_storagePermissionsLimitForeign.html
@@ -12,31 +12,33 @@
     <script type="text/javascript">
 
 task(function* () {
   yield setCookieBehavior(BEHAVIOR_LIMIT_FOREIGN);
 
   // We should be able to access storage
   yield storageAllowed();
 
-  // Same origin iframes should be prevented, unless they have chrome privileges
+  // Same origin iframes should be allowed.
   yield runIFrame("frameStorageAllowed.html");
+  yield runIFrame("frameStorageChrome.html?allowed=yes");
+
+  // Null principal iframes should not.
   yield runIFrame("frameStorageNullprincipal.sjs");
-  yield runIFrame("frameStorageChrome.html");
 
   // Sandboxed iframes should have the null principal, and thus can't access storage
   document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts');
   yield runIFrame("frameStoragePrevented.html#nullprincipal");
   yield runIFrame("frameStorageNullprincipal.sjs");
   document.querySelector('iframe').removeAttribute('sandbox');
 
-  // Thirdparty iframes should be blocked, unless they have chrome privileges
+  // Thirdparty iframes should be blocked, even when accessed from chrome over Xrays.
   yield runIFrame(thirdparty + "frameStoragePrevented.html#thirdparty");
   yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs");
-  yield runIFrame(thirdparty + "frameStorageChrome.html");
+  yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no");
 
   // Workers should be unable to access storage
   yield runWorker("workerStorageAllowed.js");
 });
 
     </script>
   </body>
 </html>
--- a/dom/tests/mochitest/general/test_storagePermissionsReject.html
+++ b/dom/tests/mochitest/general/test_storagePermissionsReject.html
@@ -12,30 +12,31 @@
     <script type="text/javascript">
 
 task(function* () {
   yield setCookieBehavior(BEHAVIOR_REJECT);
 
   // We should be unable to access storage
   yield storagePrevented();
 
-  // Same origin iframes should be prevented, unless they have chrome privileges
+  // Same origin iframes should be blocked.
   yield runIFrame("frameStoragePrevented.html");
   yield runIFrame("frameStorageNullprincipal.sjs");
-  yield runIFrame("frameStorageChrome.html");
+  yield runIFrame("frameStorageChrome.html?allowed=no&blockSessionStorage=yes");
 
   // Sandboxed iframes should have the null principal, and thus can't access storage
   document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts');
   yield runIFrame("frameStoragePrevented.html#nullprincipal");
   yield runIFrame("frameStorageNullprincipal.sjs");
   document.querySelector('iframe').removeAttribute('sandbox');
 
-  // thirdparty iframes should be blocked, unless they have chrome privileges
+  // thirdparty iframes should be blocked.
   yield runIFrame(thirdparty + "frameStoragePrevented.html");
   yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs");
+  yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no&blockSessionStorage=yes");
 
   // Workers should be unable to access storage
   yield runWorker("workerStoragePrevented.js");
 });
 
     </script>
   </body>
 </html>
--- a/dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html
+++ b/dom/tests/mochitest/general/test_storagePermissionsRejectForeign.html
@@ -15,28 +15,28 @@ task(function* () {
   yield setCookieBehavior(BEHAVIOR_REJECT_FOREIGN);
 
   // We should be able to access storage
   yield storageAllowed();
 
   // Same origin iframes should be allowed, unless they redirect to a URI with the null principal
   yield runIFrame("frameStorageAllowed.html");
   yield runIFrame("frameStorageNullprincipal.sjs");
-  yield runIFrame("frameStorageChrome.html");
+  yield runIFrame("frameStorageChrome.html?allowed=yes");
 
   // Sandboxed iframes should have the null principal, and thus can't access storage
   document.querySelector('iframe').setAttribute('sandbox', 'allow-scripts');
   yield runIFrame("frameStoragePrevented.html#nullprincipal");
   yield runIFrame("frameStorageNullprincipal.sjs");
   document.querySelector('iframe').removeAttribute('sandbox');
 
-  // thirdparty iframes should be blocked, unless they have chrome privileges
+  // thirdparty iframes should be blocked.
   yield runIFrame(thirdparty + "frameStoragePrevented.html#thirdparty");
   yield runIFrame(thirdparty + "frameStorageNullprincipal.sjs");
-  yield runIFrame(thirdparty + "frameStorageChrome.html");
+  yield runIFrame(thirdparty + "frameStorageChrome.html?allowed=no");
 
   // Workers should be able to access storage
   yield runWorker("workerStorageAllowed.js");
 });
 
     </script>
   </body>
 </html>
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -427,17 +427,17 @@ partial interface Navigator {
 };
 
 partial interface Navigator {
   [Throws, Pref="dom.inputport.enabled", CheckAnyPermissions="inputport", AvailableIn=CertifiedApps]
   readonly attribute InputPortManager inputPortManager;
 };
 
 partial interface Navigator {
-  [Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", AvailableIn="PrivilegedApps", SameObject]
+  [Throws, Pref="dom.presentation.enabled", CheckAnyPermissions="presentation", SameObject]
   readonly attribute Presentation? presentation;
 };
 
 partial interface Navigator {
   [NewObject, Pref="dom.mozTCPSocket.enabled", CheckAnyPermissions="tcp-socket"]
   readonly attribute LegacyMozTCPSocket mozTCPSocket;
 };
 
--- a/dom/webidl/Presentation.webidl
+++ b/dom/webidl/Presentation.webidl
@@ -1,17 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
+ CheckAnyPermissions="presentation"]
 interface Presentation : EventTarget {
  /*
   * This should be used by the UA as the default presentation request for the
   * controller. When the UA wishes to initiate a PresentationSession on the
   * controller's behalf, it MUST start a presentation session using the default
   * presentation request (as if the controller had called |defaultRequest.start()|).
   *
   * Only used by controlling browsing context (senders).
--- a/dom/webidl/PresentationAvailability.webidl
+++ b/dom/webidl/PresentationAvailability.webidl
@@ -1,17 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
+ CheckAnyPermissions="presentation"]
 interface PresentationAvailability : EventTarget {
   /*
    * If there is at least one device discovered by UA, the value is |true|.
    * Otherwise, its value should be |false|.
    */
   readonly attribute boolean value;
 
   /*
--- a/dom/webidl/PresentationRequest.webidl
+++ b/dom/webidl/PresentationRequest.webidl
@@ -1,18 +1,17 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  */
 
 [Constructor(DOMString url),
  Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
+ CheckAnyPermissions="presentation"]
 interface PresentationRequest : EventTarget {
   /*
    * A requesting page use start() to start a new session, and the session will
    * be returned with the promise. UA may show a prompt box with a list of
    * available devices and ask the user to grant permission, choose a device, or
    * cancel the operation.
    *
    * The promise is resolved when the presenting page is successfully loaded and
--- a/dom/webidl/PresentationSession.webidl
+++ b/dom/webidl/PresentationSession.webidl
@@ -13,18 +13,17 @@ enum PresentationSessionState
   "disconnected",
 
   // The presentation is nonexistent anymore. It could be terminated manually,
   // or either requesting page or presenting page is no longer available.
   "terminated"
 };
 
 [Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
+ CheckAnyPermissions="presentation"]
 interface PresentationSession : EventTarget {
   /*
    * Unique id for all existing sessions.
    */
   [Constant]
   readonly attribute DOMString id;
 
   /*
--- a/dom/webidl/PresentationSessionConnectEvent.webidl
+++ b/dom/webidl/PresentationSessionConnectEvent.webidl
@@ -2,18 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Constructor(DOMString type,
              optional PresentationSessionConnectEventInit eventInitDict),
  Pref="dom.presentation.enabled",
- CheckAnyPermissions="presentation",
- AvailableIn="PrivilegedApps"]
+ CheckAnyPermissions="presentation"]
 interface PresentationSessionConnectEvent : Event
 {
   [SameObject]
   readonly attribute PresentationSession session;
 };
 
 dictionary PresentationSessionConnectEventInit : EventInit
 {
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3977,30 +3977,32 @@ public:
     return NS_OK;
   }
 };
 
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
 
-void
-ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
-                                         nsIDocument* aDoc,
-                                         nsIInterceptedChannel* aChannel,
-                                         bool aIsReload,
-                                         ErrorResult& aRv)
+already_AddRefed<nsIRunnable>
+ServiceWorkerManager::PrepareFetchEvent(const OriginAttributes& aOriginAttributes,
+                                        nsIDocument* aDoc,
+                                        nsIInterceptedChannel* aChannel,
+                                        bool aIsReload,
+                                        ErrorResult& aRv)
 {
   MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsISupports> serviceWorker;
 
   bool isNavigation = false;
   aRv = aChannel->GetIsNavigation(&isNavigation);
   if (NS_WARN_IF(aRv.Failed())) {
-    return;
+    return nullptr;
   }
 
   // if the ServiceWorker script fails to load for some reason, just resume
   // the original channel.
   nsCOMPtr<nsIRunnable> failRunnable =
     NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
 
   nsAutoPtr<ServiceWorkerClientInfo> clientInfo;
@@ -4009,77 +4011,89 @@ ServiceWorkerManager::DispatchFetchEvent
     MOZ_ASSERT(aDoc);
     aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
                                 getter_AddRefs(serviceWorker));
     clientInfo = new ServiceWorkerClientInfo(aDoc);
   } else {
     nsCOMPtr<nsIChannel> internalChannel;
     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
     if (NS_WARN_IF(aRv.Failed())) {
-      return;
+      return nullptr;
     }
 
     nsCOMPtr<nsIURI> uri;
     aRv = internalChannel->GetURI(getter_AddRefs(uri));
     if (NS_WARN_IF(aRv.Failed())) {
-      return;
+      return nullptr;
     }
 
     nsRefPtr<ServiceWorkerRegistrationInfo> registration =
       GetServiceWorkerRegistrationInfo(aOriginAttributes, uri);
     if (!registration) {
       NS_WARNING("No registration found when dispatching the fetch event");
       aRv.Throw(NS_ERROR_FAILURE);
-      return;
+      return nullptr;
     }
 
     // This should only happen if IsAvailable() returned true.
     MOZ_ASSERT(registration->mActiveWorker);
 
     nsRefPtr<ServiceWorker> sw;
     aRv = CreateServiceWorker(registration->mPrincipal,
                               registration->mActiveWorker,
                               failRunnable,
                               getter_AddRefs(sw));
     serviceWorker = sw.forget();
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
-    return;
+    return nullptr;
   }
 
   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
     new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
 
   nsRefPtr<ServiceWorker> sw = static_cast<ServiceWorker*>(serviceWorker.get());
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(sw));
 
   nsCOMPtr<nsIRunnable> continueRunnable =
     new ContinueDispatchFetchEventRunnable(sw->GetWorkerPrivate(), handle,
                                            serviceWorkerHandle, clientInfo,
                                            aIsReload);
 
+  return continueRunnable.forget();
+}
+
+void
+ServiceWorkerManager::DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
+                                                 nsIRunnable* aPreparedRunnable,
+                                                 ErrorResult& aRv)
+{
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aPreparedRunnable);
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIChannel> innerChannel;
   aRv = aChannel->GetChannel(getter_AddRefs(innerChannel));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
 
   // If there is no upload stream, then continue immediately
   if (!uploadChannel) {
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(continueRunnable->Run()));
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPreparedRunnable->Run()));
     return;
   }
 
   // Otherwise, ensure the upload stream can be cloned directly.  This may
   // require some async copying, so provide a callback.
-  aRv = uploadChannel->EnsureUploadStreamIsCloneable(continueRunnable);
+  aRv = uploadChannel->EnsureUploadStreamIsCloneable(aPreparedRunnable);
 }
 
 bool
 ServiceWorkerManager::IsAvailable(const OriginAttributes& aOriginAttributes,
                                   nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -310,22 +310,27 @@ public:
   nsTHashtable<nsISupportsHashKey> mAllDocuments;
 
   bool
   IsAvailable(const OriginAttributes& aOriginAttributes, nsIURI* aURI);
 
   bool
   IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
 
+  already_AddRefed<nsIRunnable>
+  PrepareFetchEvent(const OriginAttributes& aOriginAttributes,
+                    nsIDocument* aDoc,
+                    nsIInterceptedChannel* aChannel,
+                    bool aIsReload,
+                    ErrorResult& aRv);
+
   void
-  DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
-                     nsIDocument* aDoc,
-                     nsIInterceptedChannel* aChannel,
-                     bool aIsReload,
-                     ErrorResult& aRv);
+  DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
+                             nsIRunnable* aPreparedRunnable,
+                             ErrorResult& aRv);
 
   void
   SoftUpdate(nsIPrincipal* aPrincipal,
              const nsACString& aScope,
              ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
 
   void
   SoftUpdate(const OriginAttributes& aOriginAttributes,
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -106,22 +106,22 @@ public:
 
     nsCOMPtr<nsILoadGroup> loadGroup;
     rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), aPrincipal);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Note that because there is no "serviceworker" RequestContext type, we can
-    // use the external TYPE_SCRIPT content policy types when loading a service
+    // use the TYPE_INTERNAL_SCRIPT content policy types when loading a service
     // worker.
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri, aPrincipal,
                        nsILoadInfo::SEC_NORMAL,
-                       nsIContentPolicy::TYPE_SCRIPT,
+                       nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
                        loadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsLoadFlags flags;
     rv = mChannel->GetLoadFlags(&flags);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/xbl/nsXBLResourceLoader.cpp
+++ b/dom/xbl/nsXBLResourceLoader.cpp
@@ -146,17 +146,17 @@ nsXBLResourceLoader::LoadResources(bool*
           {
             rv = StyleSheetLoaded(sheet, false, NS_OK);
             NS_ASSERTION(NS_SUCCEEDED(rv), "Processing the style sheet failed!!!");
           }
         }
       }
       else
       {
-        rv = cssLoader->LoadSheet(url, docPrincipal, EmptyCString(), this);
+        rv = cssLoader->LoadSheet(url, false, docPrincipal, EmptyCString(), this);
         if (NS_SUCCEEDED(rv))
           ++mPendingSheets;
       }
     }
   }
 
   *aResult = (mPendingSheets == 0);
   mInLoadResourcesFunc = false;
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -419,17 +419,17 @@ txCompileObserver::loadURI(const nsAStri
     nsCOMPtr<nsIPrincipal> referrerPrincipal;
     rv = nsContentUtils::GetSecurityManager()->
       GetSimpleCodebasePrincipal(referrerUri,
                                  getter_AddRefs(referrerPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Content Policy
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
+    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET,
                                    uri,
                                    referrerPrincipal,
                                    mLoaderDocument,
                                    NS_LITERAL_CSTRING("application/xml"),
                                    nullptr,
                                    &shouldLoad);
     NS_ENSURE_SUCCESS(rv, rv);
     if (NS_CP_REJECTED(shouldLoad)) {
@@ -519,17 +519,17 @@ TX_LoadSheet(nsIURI* aUri, txMozillaXSLT
 
     nsAutoCString spec;
     aUri->GetSpec(spec);
     MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get()));
 
     // Content Policy
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     nsresult rv =
-        NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
+        NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET,
                                   aUri,
                                   principal,
                                   aLoaderDocument,
                                   NS_LITERAL_CSTRING("application/xml"),
                                   nullptr,
                                   &shouldLoad);
     NS_ENSURE_SUCCESS(rv, rv);
     if (NS_CP_REJECTED(shouldLoad)) {
@@ -661,17 +661,17 @@ txSyncCompileObserver::loadURI(const nsA
     nsCOMPtr<nsIPrincipal> referrerPrincipal;
     rv = nsContentUtils::GetSecurityManager()->
       GetSimpleCodebasePrincipal(referrerUri,
                                  getter_AddRefs(referrerPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Content Policy
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_STYLESHEET,
+    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET,
                                    uri,
                                    referrerPrincipal,
                                    nullptr,
                                    NS_LITERAL_CSTRING("application/xml"),
                                    nullptr,
                                    &shouldLoad);
     NS_ENSURE_SUCCESS(rv, rv);
     if (NS_CP_REJECTED(shouldLoad)) {
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3245,17 +3245,18 @@ XULDocument::LoadScript(nsXULPrototypeSc
     }
 
     // Allow security manager and content policies to veto the load. Note that
     // at this point we already lost context information of the script.
     rv = nsScriptLoader::ShouldLoadScript(
                             this,
                             static_cast<nsIDocument*>(this),
                             aScriptProto->mSrcURI,
-                            NS_LITERAL_STRING("application/x-javascript"));
+                            NS_LITERAL_STRING("application/x-javascript"),
+                            false);
     if (NS_FAILED(rv)) {
       *aBlock = false;
       return rv;
     }
 
     // Release script objects from FastLoad since we decided against using them
     aScriptProto->UnlinkJSObjects();
 
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -2786,17 +2786,17 @@ nsHTMLEditor::ReplaceStyleSheet(const ns
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
 
   nsCOMPtr<nsIURI> uaURI;
   nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return ps->GetDocument()->CSSLoader()->
-    LoadSheet(uaURI, nullptr, EmptyCString(), this);
+    LoadSheet(uaURI, false, nullptr, EmptyCString(), this);
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::RemoveStyleSheet(const nsAString &aURL)
 {
   nsRefPtr<CSSStyleSheet> sheet;
   nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/embedding/browser/nsContextMenuInfo.cpp
+++ b/embedding/browser/nsContextMenuInfo.cpp
@@ -289,17 +289,17 @@ nsContextMenuInfo::GetBackgroundImageReq
           NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE);
 
           nsRefPtr<imgLoader> il = imgLoader::GetInstance();
           NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
 
           return il->LoadImage(bgUri, nullptr, nullptr,
                                doc->GetReferrerPolicy(), principal, nullptr,
                                nullptr, nullptr, nsIRequest::LOAD_NORMAL,
-                               nullptr, nsIContentPolicy::TYPE_IMAGE,
+                               nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                EmptyString(), aRequest);
         }
       }
 
       // bail if we encounter non-transparent background-color
       computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"),
                                          getter_AddRefs(cssValue));
       primitiveValue = do_QueryInterface(cssValue);
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -331,16 +331,17 @@ SurfaceFactory::~SurfaceFactory()
 already_AddRefed<layers::SharedSurfaceTextureClient>
 SurfaceFactory::NewTexClient(const gfx::IntSize& size)
 {
     while (!mRecycleFreePool.empty()) {
         RefPtr<layers::SharedSurfaceTextureClient> cur = mRecycleFreePool.front();
         mRecycleFreePool.pop();
 
         if (cur->Surf()->mSize == size) {
+            cur->Surf()->WaitForBufferOwnership();
             return cur.forget();
         }
 
         StopRecycling(cur);
     }
 
     UniquePtr<SharedSurface> surf = Move(CreateShared(size));
     if (!surf)
rename from gfx/tests/gtest/TestAsyncPanZoomController.cpp
rename to gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
@@ -942,18 +942,18 @@ TEST_F(APZCBasicTester, Overzoom) {
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   PinchWithPinchInputAndCheckStatus(apzc, 50, 50, 0.5, true);
 
   fm = apzc->GetFrameMetrics();
   EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
   // bug 936721 - PGO builds introduce rounding error so
   // use a fuzzy match instead
-  EXPECT_LT(abs(fm.GetScrollOffset().x), 1e-5);
-  EXPECT_LT(abs(fm.GetScrollOffset().y), 1e-5);
+  EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
+  EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
 }
 
 TEST_F(APZCBasicTester, SimpleTransform) {
   ParentLayerPoint pointOut;
   ViewTransform viewTransformOut;
   apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
 
   EXPECT_EQ(ParentLayerPoint(), pointOut);
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/moz.build
@@ -0,0 +1,21 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+    'TestAsyncPanZoomController.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+LOCAL_INCLUDES += [
+    '/gfx/2d',
+    '/gfx/layers',
+    '/gfx/tests/gtest'  # for TestLayers.h, which is shared with the gfx gtests
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
rename from gfx/layers/apz/test/apz_test_native_event_utils.js
rename to gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
rename from gfx/layers/apz/test/apz_test_utils.js
rename to gfx/layers/apz/test/mochitest/apz_test_utils.js
rename from gfx/layers/apz/test/chrome.ini
rename to gfx/layers/apz/test/mochitest/chrome.ini
rename from gfx/layers/apz/test/helper_basic_pan.html
rename to gfx/layers/apz/test/mochitest/helper_basic_pan.html
rename from gfx/layers/apz/test/helper_bug1151663.html
rename to gfx/layers/apz/test/mochitest/helper_bug1151663.html
rename from gfx/layers/apz/test/helper_bug982141.html
rename to gfx/layers/apz/test/mochitest/helper_bug982141.html
rename from gfx/layers/apz/test/helper_div_pan.html
rename to gfx/layers/apz/test/mochitest/helper_div_pan.html
rename from gfx/layers/apz/test/helper_iframe1.html
rename to gfx/layers/apz/test/mochitest/helper_iframe1.html
rename from gfx/layers/apz/test/helper_iframe2.html
rename to gfx/layers/apz/test/mochitest/helper_iframe2.html
rename from gfx/layers/apz/test/helper_iframe_pan.html
rename to gfx/layers/apz/test/mochitest/helper_iframe_pan.html
rename from gfx/layers/apz/test/helper_subframe_style.css
rename to gfx/layers/apz/test/mochitest/helper_subframe_style.css
rename from gfx/layers/apz/test/mochitest.ini
rename to gfx/layers/apz/test/mochitest/mochitest.ini
rename from gfx/layers/apz/test/test_basic_pan.html
rename to gfx/layers/apz/test/mochitest/test_basic_pan.html
rename from gfx/layers/apz/test/test_bug1151663.html
rename to gfx/layers/apz/test/mochitest/test_bug1151663.html
rename from gfx/layers/apz/test/test_bug1151667.html
rename to gfx/layers/apz/test/mochitest/test_bug1151667.html
rename from gfx/layers/apz/test/test_bug982141.html
rename to gfx/layers/apz/test/mochitest/test_bug982141.html
rename from gfx/layers/apz/test/test_layerization.html
rename to gfx/layers/apz/test/mochitest/test_layerization.html
rename from gfx/layers/apz/test/test_scroll_inactive_flattened_frame.html
rename to gfx/layers/apz/test/mochitest/test_scroll_inactive_flattened_frame.html
rename from gfx/layers/apz/test/test_smoothness.html
rename to gfx/layers/apz/test/mochitest/test_smoothness.html
rename from gfx/layers/apz/test/test_wheel_scroll.html
rename to gfx/layers/apz/test/mochitest/test_wheel_scroll.html
rename from gfx/layers/apz/test/test_wheel_transactions.html
rename to gfx/layers/apz/test/mochitest/test_wheel_transactions.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-h-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-h-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-h-rtl-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-h-rtl.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-h-rtl.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-h.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-h.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-v-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-v-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-v-rtl-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-v-rtl.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-v-rtl.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-v.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-v.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-vh-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-vh-rtl.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh-rtl.html
rename from gfx/layers/apz/reftests/async-scrollbar-1-vh.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-1-vh.html
rename from gfx/layers/apz/reftests/async-scrollbar-zoom-1-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-zoom-1-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-zoom-1.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-zoom-1.html
rename from gfx/layers/apz/reftests/async-scrollbar-zoom-2-ref.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-zoom-2-ref.html
rename from gfx/layers/apz/reftests/async-scrollbar-zoom-2.html
rename to gfx/layers/apz/test/reftest/async-scrollbar-zoom-2.html
rename from gfx/layers/apz/reftests/reftest.list
rename to gfx/layers/apz/test/reftest/reftest.list
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -366,21 +366,34 @@ CanvasClientSharedSurface::Update(gfx::I
 
   MOZ_ASSERT(newFront);
   if (!newFront) {
     // May happen in a release build in case of memory pressure.
     gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
     return;
   }
 
+#ifndef MOZ_WIDGET_GONK
   if (mFront) {
     if (mFront->GetFlags() & TextureFlags::RECYCLE) {
       mFront->WaitForCompositorRecycle();
     }
   }
+#else
+  // AutoRemoveTexture does the followings.
+  // - Ensure to deliver FenceHandle from TextureHost to TextureClient, before
+  //   next TextureClient usage.
+  // - Control TextureClient's recycling timing.
+  // - Call RemoveTexture() after newFront's UseTextures() call.
+  //   It could improve performance of Host side's EGL handling on gonk
+  AutoRemoveTexture autoRemove(this);
+  if (mFront && mFront != newFront) {
+    autoRemove.mTexture = mFront;
+  }
+#endif
 
   mFront = newFront;
 
   // Add the new TexClient.
   MOZ_ALWAYS_TRUE( AddTextureClient(mFront) );
 
   nsAutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -437,29 +437,29 @@ public:
    */
   void ForceRemove(bool sync = false);
 
   virtual void SetReleaseFenceHandle(const FenceHandle& aReleaseFenceHandle)
   {
     mReleaseFenceHandle.Merge(aReleaseFenceHandle);
   }
 
-  FenceHandle GetAndResetReleaseFenceHandle()
+  virtual FenceHandle GetAndResetReleaseFenceHandle()
   {
     FenceHandle fence;
     mReleaseFenceHandle.TransferToAnotherFenceHandle(fence);
     return fence;
   }
 
   virtual void SetAcquireFenceHandle(const FenceHandle& aAcquireFenceHandle)
   {
     mAcquireFenceHandle = aAcquireFenceHandle;
   }
 
-  const FenceHandle& GetAcquireFenceHandle() const
+  virtual const FenceHandle& GetAcquireFenceHandle() const
   {
     return mAcquireFenceHandle;
   }
 
   /**
    * Set AsyncTransactionTracker of RemoveTextureFromCompositableAsync() transaction.
    */
   virtual void SetRemoveFromCompositableWaiter(AsyncTransactionWaiter* aWaiter) {}
--- a/gfx/layers/client/TextureClientSharedSurface.cpp
+++ b/gfx/layers/client/TextureClientSharedSurface.cpp
@@ -8,16 +8,20 @@
 #include "GLContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"        // for gfxDebug
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/unused.h"
 #include "nsThreadUtils.h"
 #include "SharedSurface.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include "SharedSurfaceGralloc.h"
+#endif
+
 namespace mozilla {
 namespace layers {
 
 SharedSurfaceTextureClient::SharedSurfaceTextureClient(ISurfaceAllocator* aAllocator,
                                                        TextureFlags aFlags,
                                                        UniquePtr<gl::SharedSurface> surf,
                                                        gl::SurfaceFactory* factory)
   : TextureClient(aAllocator, aFlags | TextureFlags::RECYCLE)
@@ -36,10 +40,71 @@ SharedSurfaceTextureClient::GetSize() co
 }
 
 bool
 SharedSurfaceTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   return mSurf->ToSurfaceDescriptor(&aOutDescriptor);
 }
 
+void
+SharedSurfaceTextureClient::SetReleaseFenceHandle(const FenceHandle& aReleaseFenceHandle)
+{
+#ifdef MOZ_WIDGET_GONK
+  SharedSurface_Gralloc* surf = nullptr;
+  if (mSurf->mType == SharedSurfaceType::Gralloc) {
+    surf = SharedSurface_Gralloc::Cast(mSurf.get());
+  }
+  if (surf && surf->GetTextureClient()) {
+    surf->GetTextureClient()->SetReleaseFenceHandle(aReleaseFenceHandle);
+    return;
+  }
+#endif
+  TextureClient::SetReleaseFenceHandle(aReleaseFenceHandle);
+}
+
+FenceHandle
+SharedSurfaceTextureClient::GetAndResetReleaseFenceHandle()
+{
+#ifdef MOZ_WIDGET_GONK
+  SharedSurface_Gralloc* surf = nullptr;
+  if (mSurf->mType == SharedSurfaceType::Gralloc) {
+    surf = SharedSurface_Gralloc::Cast(mSurf.get());
+  }
+  if (surf && surf->GetTextureClient()) {
+    return surf->GetTextureClient()->GetAndResetReleaseFenceHandle();
+  }
+#endif
+  return TextureClient::GetAndResetReleaseFenceHandle();
+}
+
+void
+SharedSurfaceTextureClient::SetAcquireFenceHandle(const FenceHandle& aAcquireFenceHandle)
+{
+#ifdef MOZ_WIDGET_GONK
+  SharedSurface_Gralloc* surf = nullptr;
+  if (mSurf->mType == SharedSurfaceType::Gralloc) {
+    surf = SharedSurface_Gralloc::Cast(mSurf.get());
+  }
+  if (surf && surf->GetTextureClient()) {
+    return surf->GetTextureClient()->SetAcquireFenceHandle(aAcquireFenceHandle);
+  }
+#endif
+  TextureClient::SetAcquireFenceHandle(aAcquireFenceHandle);
+}
+
+const FenceHandle&
+SharedSurfaceTextureClient::GetAcquireFenceHandle() const
+{
+#ifdef MOZ_WIDGET_GONK
+  SharedSurface_Gralloc* surf = nullptr;
+  if (mSurf->mType == SharedSurfaceType::Gralloc) {
+    surf = SharedSurface_Gralloc::Cast(mSurf.get());
+  }
+  if (surf && surf->GetTextureClient()) {
+    return surf->GetTextureClient()->GetAcquireFenceHandle();
+  }
+#endif
+  return TextureClient::GetAcquireFenceHandle();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientSharedSurface.h
+++ b/gfx/layers/client/TextureClientSharedSurface.h
@@ -59,16 +59,21 @@ public:
     MOZ_CRASH("Should never hit this.");
     return false;
   }
 
   virtual gfx::IntSize GetSize() const override;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) override;
 
+  virtual void SetReleaseFenceHandle(const FenceHandle& aReleaseFenceHandle) override;
+  virtual FenceHandle GetAndResetReleaseFenceHandle() override;
+  virtual void SetAcquireFenceHandle(const FenceHandle& aAcquireFenceHandle) override;
+  virtual const FenceHandle& GetAcquireFenceHandle() const override;
+
   gl::SharedSurface* Surf() const {
     return mSurf.get();
   }
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -401,15 +401,18 @@ CXXFLAGS += [
     '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
         'frameworks/base/include/media/stagefright',
         'frameworks/base/include/media/stagefright/openmax',
         'frameworks/av/include/media/stagefright',
         'frameworks/native/include/media/openmax',
     ]
 ]
 
-MOCHITEST_MANIFESTS += ['apz/test/mochitest.ini']
-MOCHITEST_CHROME_MANIFESTS += ['apz/test/chrome.ini']
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['apz/test/gtest']
+
+MOCHITEST_MANIFESTS += ['apz/test/mochitest/mochitest.ini']
+MOCHITEST_CHROME_MANIFESTS += ['apz/test/mochitest/chrome.ini']
 
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 CXXFLAGS += CONFIG['TK_CFLAGS']
 
 LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
--- a/gfx/moz.build
+++ b/gfx/moz.build
@@ -1,17 +1,14 @@
 # -*- 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/.
 
-with Files('tests/gtest/TestAsyncPanZoomController.cpp'):
-    BUG_COMPONENT = ('Core', 'Panning and Zooming')
-
 if CONFIG['MOZ_TREE_CAIRO']:
     DIRS += ['cairo']
 
 DIRS += [
     '2d',
     'ycbcr',
     'angle',
     'src',
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -1,13 +1,14 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+#include "gfxPrefs.h"
 #include "gfxUtils.h"
 #include "gtest/gtest.h"
 #include "TestLayers.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -3,17 +3,16 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'gfxSurfaceRefCountTest.cpp',
     # Disabled on suspicion of causing bug 904227
     #'gfxWordCacheTest.cpp',
-    'TestAsyncPanZoomController.cpp',
     'TestBufferRotation.cpp',
     'TestColorNames.cpp',
     'TestCompositor.cpp',
     'TestGfxPrefs.cpp',
     'TestGfxWidgets.cpp',
     'TestLayers.cpp',
     'TestMoz2D.cpp',
     'TestQcms.cpp',
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -35,17 +35,17 @@ inline Rect ToRect(const IntRect &aRect)
 }
 
 inline Color ToColor(const gfxRGBA &aRGBA)
 {
   return Color(Float(aRGBA.r), Float(aRGBA.g),
                Float(aRGBA.b), Float(aRGBA.a));
 }
 
-inline gfxRGBA ThebesColor(const Color &aColor)
+inline gfxRGBA ThebesColor(Color &aColor)
 {
   return gfxRGBA(aColor.r, aColor.g, aColor.b, aColor.a);
 }
 
 inline Matrix ToMatrix(const gfxMatrix &aMatrix)
 {
   return Matrix(Float(aMatrix._11), Float(aMatrix._12), Float(aMatrix._21),
                 Float(aMatrix._22), Float(aMatrix._31), Float(aMatrix._32));
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -165,109 +165,68 @@ struct BlurCacheKey : public PLDHashEntr
   typedef const BlurCacheKey* KeyTypePointer;
   enum { ALLOW_MEMMOVE = true };
 
   IntSize mMinSize;
   IntSize mBlurRadius;
   gfxRGBA mShadowColor;
   BackendType mBackend;
   RectCornerRadii mCornerRadii;
-  bool mIsInset;
 
-  // Only used for inset blurs
-  bool mHasBorderRadius;
-  gfxIntSize mSpreadRadius;
-  IntSize mInnerMinSize;
-
-  BlurCacheKey(IntSize aMinSize, gfxIntSize aBlurRadius,
+  BlurCacheKey(IntSize aMinimumSize, gfxIntSize aBlurRadius,
                RectCornerRadii* aCornerRadii, gfxRGBA aShadowColor,
-               BackendType aBackendType)
-    : BlurCacheKey(aMinSize, IntSize(0, 0),
-                   aBlurRadius, IntSize(0, 0),
-                   aCornerRadii, aShadowColor,
-                   false, false, aBackendType)
-  {}
+               BackendType aBackend)
+    : mMinSize(aMinimumSize)
+    , mBlurRadius(aBlurRadius)
+    , mShadowColor(aShadowColor)
+    , mBackend(aBackend)
+    , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
+  { }
 
   explicit BlurCacheKey(const BlurCacheKey* aOther)
     : mMinSize(aOther->mMinSize)
     , mBlurRadius(aOther->mBlurRadius)
     , mShadowColor(aOther->mShadowColor)
     , mBackend(aOther->mBackend)
     , mCornerRadii(aOther->mCornerRadii)
-    , mIsInset(aOther->mIsInset)
-    , mHasBorderRadius(aOther->mHasBorderRadius)
-    , mSpreadRadius(aOther->mSpreadRadius)
-    , mInnerMinSize(aOther->mInnerMinSize)
-  { }
-
-  explicit BlurCacheKey(IntSize aOuterMinSize, IntSize aInnerMinSize,
-                        gfxIntSize aBlurRadius, gfxIntSize aSpreadRadius,
-                        const RectCornerRadii* aCornerRadii, gfxRGBA aShadowColor,
-                        bool aIsInset,
-                        bool aHasBorderRadius, BackendType aBackendType)
-    : mMinSize(aOuterMinSize)
-    , mBlurRadius(aBlurRadius)
-    , mShadowColor(aShadowColor)
-    , mBackend(aBackendType)
-    , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii())
-    , mIsInset(aIsInset)
-    , mHasBorderRadius(aHasBorderRadius)
-    , mSpreadRadius(aSpreadRadius)
-    , mInnerMinSize(aInnerMinSize)
   { }
 
   static PLDHashNumber
   HashKey(const KeyTypePointer aKey)
   {
     PLDHashNumber hash = 0;
     hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height);
     hash = AddToHash(hash, aKey->mBlurRadius.width, aKey->mBlurRadius.height);
 
     hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.r, sizeof(gfxFloat)));
     hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.g, sizeof(gfxFloat)));
     hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.b, sizeof(gfxFloat)));
     hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.a, sizeof(gfxFloat)));
 
     for (int i = 0; i < 4; i++) {
-      hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
+    hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height);
     }
 
     hash = AddToHash(hash, (uint32_t)aKey->mBackend);
-
-    if (aKey->mIsInset) {
-      hash = AddToHash(hash, aKey->mSpreadRadius.width, aKey->mSpreadRadius.height);
-      hash = AddToHash(hash, aKey->mInnerMinSize.width, aKey->mInnerMinSize.height);
-      hash = AddToHash(hash, HashBytes(&aKey->mHasBorderRadius, sizeof(bool)));
-    }
     return hash;
   }
 
-  bool
-  KeyEquals(KeyTypePointer aKey) const
+  bool KeyEquals(KeyTypePointer aKey) const
   {
     if (aKey->mMinSize == mMinSize &&
         aKey->mBlurRadius == mBlurRadius &&
         aKey->mCornerRadii == mCornerRadii &&
         aKey->mShadowColor == mShadowColor &&
         aKey->mBackend == mBackend) {
-
-      if (mIsInset) {
-        return (mHasBorderRadius == aKey->mHasBorderRadius) &&
-                (mInnerMinSize == aKey->mInnerMinSize) &&
-                (mSpreadRadius == aKey->mSpreadRadius);
-      }
-
       return true;
      }
 
      return false;
   }
-
-  static KeyTypePointer
-  KeyToPointer(KeyType aKey)
+  static KeyTypePointer KeyToPointer(KeyType aKey)
   {
     return &aKey;
   }
 };
 
 /**
  * This class is what is cached. It need to be allocated in an object separated
  * to the cache entry to be able to be tracked by the nsExpirationTracker.
@@ -327,37 +286,16 @@ class BlurCache final : public nsExpirat
                                       aBackendType));
       if (blur) {
         MarkUsed(blur);
       }
 
       return blur;
     }
 
-    BlurCacheData* LookupInsetBoxShadow(const IntSize aOuterMinSize,
-                                        const IntSize aInnerMinSize,
-                                        const gfxIntSize& aBlurRadius,
-                                        const gfxIntSize& aSpreadRadius,
-                                        const RectCornerRadii* aCornerRadii,
-                                        const gfxRGBA& aShadowColor,
-                                        const bool& aHasBorderRadius,
-                                        BackendType aBackendType)
-    {
-      BlurCacheKey key(aOuterMinSize, aInnerMinSize,
-                       aBlurRadius, aSpreadRadius,
-                       aCornerRadii, aShadowColor,
-                       true, aHasBorderRadius, aBackendType);
-      BlurCacheData* blur = mHashEntries.Get(key);
-      if (blur) {
-        MarkUsed(blur);
-      }
-
-      return blur;
-    }
-
     // Returns true if we successfully register the blur in the cache, false
     // otherwise.
     bool RegisterEntry(BlurCacheData* aValue)
     {
       nsresult rv = AddObject(aValue);
       if (NS_FAILED(rv)) {
         // We are OOM, and we cannot track this object. We don't want stall
         // entries in the hash table (since the expiration tracker is responsible
@@ -488,33 +426,33 @@ CreateBlurMask(const IntSize& aRectSize,
 
   MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
   MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
 
   return result.forget();
 }
 
 static already_AddRefed<SourceSurface>
-CreateBoxShadow(SourceSurface* aBlurMask, const gfxRGBA& aShadowColor)
+CreateBoxShadow(DrawTarget& aDT, SourceSurface* aBlurMask, const gfxRGBA& aShadowColor)
 {
   IntSize blurredSize = aBlurMask->GetSize();
   gfxPlatform* platform = gfxPlatform::GetPlatform();
   RefPtr<DrawTarget> boxShadowDT =
     platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8);
 
   if (!boxShadowDT) {
     return nullptr;
   }
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
 }
 
-static SourceSurface*
+SourceSurface*
 GetBlur(DrawTarget& aDT,
         const IntSize& aRectSize,
         const gfxIntSize& aBlurRadius,
         RectCornerRadii* aCornerRadii,
         const gfxRGBA& aShadowColor,
         IntMargin& aExtendDestBy,
         IntMargin& aSlice)
 {
@@ -537,17 +475,17 @@ GetBlur(DrawTarget& aDT,
 
   RefPtr<SourceSurface> blurMask =
     CreateBlurMask(aRectSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT);
 
   if (!blurMask) {
     return nullptr;
   }
 
-  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(blurMask, aShadowColor);
+  RefPtr<SourceSurface> boxShadow = CreateBoxShadow(aDT, blurMask, aShadowColor);
   if (!boxShadow) {
     return nullptr;
   }
 
   CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow);
   return boxShadow;
 }
 
@@ -598,78 +536,16 @@ DrawCorner(DrawTarget& aDT, SourceSurfac
 {
   if (aSkipRect.Contains(aDest)) {
     return;
   }
 
   aDT.DrawSurface(aSurface, aDest, aSrc);
 }
 
-static void
-DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur,
-               Rect aDstOuter, Rect aDstInner, Rect aSrcOuter, Rect aSrcInner,
-               Rect aSkipRect)
-{
-  // Corners: top left, top right, bottom left, bottom right
-  DrawCorner(aDestDrawTarget, aSourceBlur,
-             RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.X(),
-                               aDstInner.Y(), aDstOuter.X()),
-             RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.X(),
-                               aSrcInner.Y(), aSrcOuter.X()),
-             aSkipRect);
-
-  DrawCorner(aDestDrawTarget, aSourceBlur,
-             RectWithEdgesTRBL(aDstOuter.Y(), aDstOuter.XMost(),
-                               aDstInner.Y(), aDstInner.XMost()),
-             RectWithEdgesTRBL(aSrcOuter.Y(), aSrcOuter.XMost(),
-                               aSrcInner.Y(), aSrcInner.XMost()),
-             aSkipRect);
-
-  DrawCorner(aDestDrawTarget, aSourceBlur,
-             RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.X(),
-                               aDstOuter.YMost(), aDstOuter.X()),
-             RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.X(),
-                               aSrcOuter.YMost(), aSrcOuter.X()),
-             aSkipRect);
-
-  DrawCorner(aDestDrawTarget, aSourceBlur,
-             RectWithEdgesTRBL(aDstInner.YMost(), aDstOuter.XMost(),
-                               aDstOuter.YMost(), aDstInner.XMost()),
-             RectWithEdgesTRBL(aSrcInner.YMost(), aSrcOuter.XMost(),
-                               aSrcOuter.YMost(), aSrcInner.XMost()),
-             aSkipRect);
-
-  // Edges: top, left, right, bottom
-  RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
-                         RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.XMost(),
-                                           aDstInner.Y(), aDstInner.X()),
-                         RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(),
-                                           aSrcInner.Y(), aSrcInner.X()),
-                         aSkipRect);
-  RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
-                         RectWithEdgesTRBL(aDstInner.Y(), aDstInner.X(),
-                                           aDstInner.YMost(), aDstOuter.X()),
-                         RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(),
-                                           aSrcInner.YMost(), aSrcOuter.X()),
-                         aSkipRect);
-  RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
-                         RectWithEdgesTRBL(aDstInner.Y(), aDstOuter.XMost(),
-                                           aDstInner.YMost(), aDstInner.XMost()),
-                         RectWithEdgesTRBL(aSrcInner.Y(), aSrcOuter.XMost(),
-                                           aSrcInner.YMost(), aSrcInner.XMost()),
-                         aSkipRect);
-  RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur,
-                         RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.XMost(),
-                                           aDstOuter.YMost(), aDstInner.X()),
-                         RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.XMost(),
-                                           aSrcOuter.YMost(), aSrcInner.X()),
-                         aSkipRect);
-}
-
-
 /***
  * We draw a blurred a rectangle by only blurring a smaller rectangle and
  * splitting the rectangle into 9 parts.
  * First, a small minimum source rect is calculated and used to create a blur
  * mask since the actual blurring itself is expensive. Next, we use the mask
  * with the given shadow color to create a minimally-sized box shadow of the
  * right color. Finally, we cut out the 9 parts from the box-shadow source and
  * paint each part in the right place, stretching the non-corner parts to fill
@@ -717,18 +593,70 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContex
 
   Rect skipRect = ToRect(aSkipRect);
 
   if (srcInner.IsEqualInterior(srcOuter)) {
     MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter));
     // The target rect is smaller than the minimal size so just draw the surface
     destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner);
   } else {
-    DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner,
-                   srcOuter, srcInner, skipRect);
+    // Corners: top left, top right, bottom left, bottom right
+    DrawCorner(destDrawTarget, boxShadow,
+               RectWithEdgesTRBL(dstOuter.Y(), dstInner.X(),
+                                 dstInner.Y(), dstOuter.X()),
+               RectWithEdgesTRBL(srcOuter.Y(), srcInner.X(),
+                                 srcInner.Y(), srcOuter.X()),
+               skipRect);
+
+    DrawCorner(destDrawTarget, boxShadow,
+               RectWithEdgesTRBL(dstOuter.Y(), dstOuter.XMost(),
+                                 dstInner.Y(), dstInner.XMost()),
+               RectWithEdgesTRBL(srcOuter.Y(), srcOuter.XMost(),
+                                 srcInner.Y(), srcInner.XMost()),
+               skipRect);
+
+    DrawCorner(destDrawTarget, boxShadow,
+               RectWithEdgesTRBL(dstInner.YMost(), dstInner.X(),
+                                 dstOuter.YMost(), dstOuter.X()),
+               RectWithEdgesTRBL(srcInner.YMost(), srcInner.X(),
+                                 srcOuter.YMost(), srcOuter.X()),
+               skipRect);
+
+    DrawCorner(destDrawTarget, boxShadow,
+               RectWithEdgesTRBL(dstInner.YMost(), dstOuter.XMost(),
+                                 dstOuter.YMost(), dstInner.XMost()),
+               RectWithEdgesTRBL(srcInner.YMost(), srcOuter.XMost(),
+                                 srcOuter.YMost(), srcInner.XMost()),
+               skipRect);
+
+    // Edges: top, left, right, bottom
+    RepeatOrStretchSurface(destDrawTarget, boxShadow,
+                           RectWithEdgesTRBL(dstOuter.Y(), dstInner.XMost(),
+                                             dstInner.Y(), dstInner.X()),
+                           RectWithEdgesTRBL(srcOuter.Y(), srcInner.XMost(),
+                                             srcInner.Y(), srcInner.X()),
+                           skipRect);
+    RepeatOrStretchSurface(destDrawTarget, boxShadow,
+                           RectWithEdgesTRBL(dstInner.Y(), dstInner.X(),
+                                             dstInner.YMost(), dstOuter.X()),
+                           RectWithEdgesTRBL(srcInner.Y(), srcInner.X(),
+                                             srcInner.YMost(), srcOuter.X()),
+                           skipRect);
+    RepeatOrStretchSurface(destDrawTarget, boxShadow,
+                           RectWithEdgesTRBL(dstInner.Y(), dstOuter.XMost(),
+                                             dstInner.YMost(), dstInner.XMost()),
+                           RectWithEdgesTRBL(srcInner.Y(), srcOuter.XMost(),
+                                             srcInner.YMost(), srcInner.XMost()),
+                           skipRect);
+    RepeatOrStretchSurface(destDrawTarget, boxShadow,
+                           RectWithEdgesTRBL(dstInner.YMost(), dstInner.XMost(),
+                                             dstOuter.YMost(), dstInner.X()),
+                           RectWithEdgesTRBL(srcInner.YMost(), srcInner.XMost(),
+                                             srcOuter.YMost(), srcInner.X()),
+                           skipRect);
 
     // Middle part
     RepeatOrStretchSurface(destDrawTarget, boxShadow,
                            RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(),
                                              dstInner.YMost(), dstInner.X()),
                            RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(),
                                              srcInner.YMost(), srcInner.X()),
                            skipRect);
@@ -749,263 +677,8 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContex
   // will not just fill the pixels that have their pixel center inside the
   // filled shape. Instead, it will fill all the pixels which are partially
   // covered by the shape. So for pixels on the edge between two adjacent parts,
   // all those pixels will be painted to by both parts, which looks very bad.
 
   destDrawTarget.PopClip();
 }
 
-static already_AddRefed<Path>
-GetBoxShadowInsetPath(DrawTarget* aDrawTarget,
-                      const Rect aOuterRect, const Rect aInnerRect,
-                      const bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii)
-{
-  /***
-   * We create an inset path by having two rects.
-   *
-   *  -----------------------
-   *  |  ________________   |
-   *  | |                |  |
-   *  | |                |  |
-   *  | ------------------  |
-   *  |_____________________|
-   *
-   * The outer rect and the inside rect. The path
-   * creates a frame around the content where we draw the inset shadow.
-   */
-  RefPtr<PathBuilder> builder =
-    aDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD);
-  AppendRectToPath(builder, aOuterRect, true);
-
-  if (aHasBorderRadius) {
-    AppendRoundedRectToPath(builder, aInnerRect, aInnerClipRadii, false);
-  } else {
-    AppendRectToPath(builder, aInnerRect, false);
-  }
-  return builder->Finish();
-}
-
-static void
-ComputeRectsForInsetBoxShadow(gfxIntSize aBlurRadius,
-                              gfxIntSize aSpreadRadius,
-                              const Rect& aDestRect,
-                              const Rect& aShadowClipRect,
-                              Rect& aOutOuterRect,
-                              Rect& aOutInnerRect,
-                              Margin& aOutPathMargins)
-{
-  gfxIntSize marginSize = aBlurRadius + aSpreadRadius;
-  // The sizes we're given for aBlurRadius/aSpreadRadius are radius'.
-  // We actually want to paint the whole blur, so we need the diameter.
-  // We render both the outer / inner blur portions of a blur,
-  // Then we clip out the outer portion later.
-  aOutPathMargins.SizeTo(marginSize.height, marginSize.width, marginSize.height, marginSize.width);
-  aOutPathMargins += aOutPathMargins;
-
-  aOutOuterRect.x = 0;
-  aOutInnerRect.x = marginSize.width;
-
-  aOutOuterRect.y = 0;
-  aOutInnerRect.y = marginSize.height;
-
-  // + 1 for the middle edges so we can sample them
-  aOutInnerRect.width = aOutPathMargins.LeftRight() + 1;
-  aOutInnerRect.height = aOutPathMargins.TopBottom() + 1;
-
-  // The outer path rect needs to be 1 blur radius past the inner edges
-  aOutOuterRect.width = aOutInnerRect.XMost() + marginSize.width;
-  aOutOuterRect.height = aOutInnerRect.YMost() + marginSize.height;
-
-  if ((aOutOuterRect.width >= aDestRect.width) ||
-      (aOutOuterRect.height >= aDestRect.height) ||
-      (aOutInnerRect.width >= aShadowClipRect.width) ||
-      (aOutInnerRect.height >= aShadowClipRect.height))
-  {
-    aOutOuterRect.width = aDestRect.width;
-    aOutOuterRect.height = aDestRect.height;
-    aOutInnerRect.width = aShadowClipRect.width;
-    aOutInnerRect.height = aShadowClipRect.height;
-    aOutPathMargins.SizeTo(0, 0, 0, 0);
-  }
-}
-
-static void
-FillDestinationPath(gfxContext* aDestinationCtx,
-                    const Rect aDestinationRect,
-                    const Rect aShadowClipRect,
-                    const Color& aShadowColor,
-                    const bool aHasBorderRadius,
-                    const RectCornerRadii& aInnerClipRadii)
-{
-  // When there is no blur radius, fill the path onto the destination
-  // surface.
-  aDestinationCtx->SetColor(ThebesColor(aShadowColor));
-  DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
-  RefPtr<Path> shadowPath = GetBoxShadowInsetPath(destDrawTarget, aDestinationRect,
-                                                  aShadowClipRect, aHasBorderRadius,
-                                                  aInnerClipRadii);
-
-  aDestinationCtx->SetPath(shadowPath);
-  aDestinationCtx->Fill();
-}
-
-void
-CacheInsetBlur(const IntSize aMinOuterSize,
-               const IntSize aMinInnerSize,
-               const gfxIntSize& aBlurRadius,
-               const gfxIntSize& aSpreadRadius,
-               const RectCornerRadii* aCornerRadii,
-               const gfxRGBA& aShadowColor,
-               const bool& aHasBorderRadius,
-               BackendType aBackendType,
-               IntMargin aExtendBy,
-               SourceSurface* aBoxShadow)
-{
-  BlurCacheKey key(aMinOuterSize, aMinInnerSize,
-                   aBlurRadius, aSpreadRadius,
-                   aCornerRadii, aShadowColor,
-                   true, aHasBorderRadius, aBackendType);
-  BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendBy, key);
-  if (!gBlurCache->RegisterEntry(data)) {
-    delete data;
-  }
-}
-
-already_AddRefed<mozilla::gfx::SourceSurface>
-gfxAlphaBoxBlur::GetInsetBlur(Rect& aOuterRect,
-                              Rect& aInnerRect,
-                              const gfxIntSize& aBlurRadius,
-                              const gfxIntSize& aSpreadRadius,
-                              const RectCornerRadii& aInnerClipRadii,
-                              const Color& aShadowColor,
-                              const bool& aHasBorderRadius,
-                              IntPoint& aOutTopLeft,
-                              gfxContext* aDestinationCtx)
-
-{
-  if (!gBlurCache) {
-    gBlurCache = new BlurCache();
-  }
-
-  gfxIntSize outerRectSize = RoundedToInt(aOuterRect).Size();
-  gfxIntSize innerRectSize = RoundedToInt(aInnerRect).Size();
-  DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
-
-  BlurCacheData* cached =
-      gBlurCache->LookupInsetBoxShadow(outerRectSize, innerRectSize, aBlurRadius, aSpreadRadius,
-                                       &aInnerClipRadii, ThebesColor(aShadowColor),
-                                       aHasBorderRadius, destDrawTarget->GetBackendType());
-
-  if (cached) {
-    IntMargin extends = cached->mExtendDest;
-    aOutTopLeft.x = extends.left;
-    aOutTopLeft.y = extends.top;
-    // So we don't forget the actual cached blur
-    RefPtr<SourceSurface> cachedBlur = cached->mBlur;
-    return cachedBlur.forget();
-  }
-
-  // Dirty rect and skip rect are null for the min inset shadow.
-  // When rendering inset box shadows, we respect the spread radius by changing
-  // the shape of the unblurred shadow, and can pass a spread radius of zero here.
-  gfxIntSize zeroSpread(0, 0);
-  gfxContext* minGfxContext = Init(ThebesRect(aOuterRect), zeroSpread, aBlurRadius, nullptr, nullptr);
-  if (!minGfxContext) {
-    return nullptr;
-  }
-
-  DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget();
-  RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, aOuterRect,
-                                                aInnerRect, aHasBorderRadius,
-                                                aInnerClipRadii);
-
-  minGfxContext->SetColor(ThebesColor(aShadowColor));
-  minGfxContext->SetPath(maskPath);
-  minGfxContext->Fill();
-
-  RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &aOutTopLeft);
-  if (!minMask) {
-    return nullptr;
-  }
-
-  RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, ThebesColor(aShadowColor));
-  if (!minInsetBlur) {
-    return nullptr;
-  }
-
-  IntMargin extendBy(aOutTopLeft.y, 0, 0, aOutTopLeft.x);
-  CacheInsetBlur(outerRectSize, innerRectSize,
-                 aBlurRadius, aSpreadRadius,
-                 &aInnerClipRadii, ThebesColor(aShadowColor),
-                 aHasBorderRadius, destDrawTarget->GetBackendType(),
-                 extendBy, minInsetBlur);
-  return minInsetBlur.forget();
-}
-
-/***
- * Blur an inset box shadow by doing:
- * 1) Create a minimal box shadow path that creates a frame.
- * 2) Draw the box shadow portion over the destination surface.
- * 3) The "inset" part is created by a clip rect that properly clips
- *    the alpha mask so that it has clean edges. We still create the full
- *    proper alpha mask, but let the clip deal with the clean edges.
- *
- * All parameters should already be in device pixels.
- */
-void
-gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx,
-                              const Rect aDestinationRect,
-                              const Rect aShadowClipRect,
-                              const gfxIntSize aBlurRadius,
-                              const gfxIntSize aSpreadRadius,
-                              const Color& aShadowColor,
-                              bool aHasBorderRadius,
-                              const RectCornerRadii& aInnerClipRadii,
-                              const Rect aSkipRect)
-{
-  if ((aBlurRadius.width <= 0 && aBlurRadius.height <= 0)) {
-    // The outer path must be rounded out
-    // If not blurring, we're done now.
-    Rect pathRect(aDestinationRect);
-    pathRect.RoundOut();
-    FillDestinationPath(aDestinationCtx, pathRect, aShadowClipRect,
-        aShadowColor, aHasBorderRadius, aInnerClipRadii);
-    return;
-  }
-
-  DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget();
-  Rect outerRect;
-  Rect innerRect;
-  Margin pathMargins;
-  ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius,
-                                aDestinationRect, aShadowClipRect,
-                                outerRect, innerRect,
-                                pathMargins);
-  IntPoint topLeft;
-  RefPtr<SourceSurface> minInsetBlur = GetInsetBlur(outerRect, innerRect,
-                                                    aBlurRadius, aSpreadRadius,
-                                                    aInnerClipRadii, aShadowColor,
-                                                    aHasBorderRadius,
-                                                    topLeft, aDestinationCtx);
-  if (!minInsetBlur) {
-    return;
-  }
-
-  Rect destRectOuter(aDestinationRect);
-  destRectOuter.RoundIn();
-  Rect destRectInner(destRectOuter);
-  destRectInner.Deflate(pathMargins);
-
-  Rect srcRectOuter(outerRect);
-  srcRectOuter.MoveBy(abs(topLeft.x), abs(topLeft.y));
-  Rect srcRectInner(srcRectOuter);
-  srcRectInner.Deflate(pathMargins);
-
-  if (srcRectOuter.IsEqualInterior(srcRectInner)) {
-    destDrawTarget->DrawSurface(minInsetBlur, destRectOuter, srcRectOuter);
-  } else {
-    DrawBoxShadows(*destDrawTarget, minInsetBlur,
-                   destRectOuter, destRectInner,
-                   srcRectOuter, srcRectInner,
-                   aSkipRect);
- }
-}
--- a/gfx/thebes/gfxBlur.h
+++ b/gfx/thebes/gfxBlur.h
@@ -15,17 +15,16 @@
 
 class gfxContext;
 struct gfxRect;
 struct gfxRGBA;
 
 namespace mozilla {
   namespace gfx {
     class AlphaBoxBlur;
-    struct Color;
     struct RectCornerRadii;
     class SourceSurface;
     class DrawTarget;
   } // namespace gfx
 } // namespace mozilla
 
 /**
  * Implementation of a triple box blur approximation of a Gaussian blur.
@@ -131,54 +130,19 @@ public:
                               RectCornerRadii* aCornerRadii,
                               const gfxPoint& aBlurStdDev,
                               const gfxRGBA& aShadowColor,
                               const gfxRect& aDirtyRect,
                               const gfxRect& aSkipRect);
 
     static void ShutdownBlurCache();
 
-    /***
-     * Blurs an inset box shadow according to a given path.
-     * This is equivalent to calling Init(), drawing the inset path,
-     * and calling paint. Do not call Init() if using this method.
-     *
-     * @param aDestinationCtx     The destination to blur to.
-     * @param aDestinationRect    The destination rect in device pixels
-     * @param aShadowClipRect     The destiniation inner rect of the
-     *                            inset path in device pixels.
-     * @param aBlurRadius         The standard deviation of the blur.
-     * @param aSpreadRadius       The spread radius in device pixels.
-     * @param aShadowColor        The color of the blur.
-     * @param aHasBorderRadius    If this element also has a border radius
-     * @param aInnerClipRadii     Corner radii for the inside rect if it is a rounded rect.
-     * @param aSkipRect           An area in device pixels we don't have to paint in.
-     */
-    void BlurInsetBox(gfxContext* aDestinationCtx,
-                      const mozilla::gfx::Rect aDestinationRect,
-                      const mozilla::gfx::Rect aShadowClipRect,
-                      const gfxIntSize aBlurRadius,
-                      const gfxIntSize aSpreadRadius,
-                      const mozilla::gfx::Color& aShadowColor,
-                      const bool aHasBorderRadius,
-                      const RectCornerRadii& aInnerClipRadii,
-                      const mozilla::gfx::Rect aSkipRect);
+
 
 protected:
-    already_AddRefed<mozilla::gfx::SourceSurface>
-                   GetInsetBlur(mozilla::gfx::Rect& aOuterRect,
-                                mozilla::gfx::Rect& aInnerRect,
-                                const gfxIntSize& aBlurRadius,
-                                const gfxIntSize& aSpreadRadius,
-                                const RectCornerRadii& aInnerClipRadii,
-                                const mozilla::gfx::Color& aShadowColor,
-                                const bool& aHasBorderRadius,
-                                mozilla::gfx::IntPoint& aOutTopLeft,
-                                gfxContext* aDestinationCtx);
-
     /**
      * The context of the temporary alpha surface.
      */
     nsRefPtr<gfxContext> mContext;
 
     /**
      * The temporary alpha surface.
      */
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -613,17 +613,17 @@ ShouldLoadCachedImage(imgRequest* aImgRe
    * mHadInsecureRedirect flag
    */
   bool insecureRedirect = aImgRequest->HadInsecureRedirect();
   nsCOMPtr<nsIURI> contentLocation;
   aImgRequest->GetCurrentURI(getter_AddRefs(contentLocation));
   nsresult rv;
 
   int16_t decision = nsIContentPolicy::REJECT_REQUEST;
-  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_IMAGE,
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                  contentLocation,
                                  aLoadingPrincipal,
                                  aLoadingContext,
                                  EmptyCString(), //mime guess
                                  nullptr, //aExtra
                                  &decision,
                                  nsContentUtils::GetContentPolicy(),
                                  nsContentUtils::GetSecurityManager());
@@ -1976,17 +1976,17 @@ imgLoader::LoadImageXPCOM(nsIURI* aURI,
                           nsISupports* aCX,
                           nsLoadFlags aLoadFlags,
                           nsISupports* aCacheKey,
                           nsContentPolicyType aContentPolicyType,
                           imgIRequest** _retval)
 {
     // Optional parameter, so defaults to 0 (== TYPE_INVALID)
     if (!aContentPolicyType) {
-      aContentPolicyType = nsIContentPolicy::TYPE_IMAGE;
+      aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
     }
     imgRequestProxy* proxy;
     ReferrerPolicy refpol = ReferrerPolicyFromString(aReferrerPolicy);
     nsresult rv = LoadImage(aURI,
                             aInitialDocumentURI,
                             aReferrerURI,
                             refpol,
                             aLoadingPrincipal,
--- a/ipc/chromium/src/base/time_posix.cc
+++ b/ipc/chromium/src/base/time_posix.cc
@@ -94,23 +94,29 @@ Time Time::FromExploded(bool is_local, c
     // return are used here instead of values outside that range to allow for
     // proper round-tripping between exploded and counter-type time
     // representations in the presence of possible truncation to time_t by
     // division and use with other functions that accept time_t.
     //
     // When representing the most distant time in the future, add in an extra
     // 999ms to avoid the time being less than any other possible value that
     // this function can return.
+
+    // Take care to avoid overflows when time_t is int64_t.
     if (exploded.year < 1969) {
-      milliseconds = std::numeric_limits<time_t>::min() *
-                     kMillisecondsPerSecond;
+      int64_t min_seconds = (sizeof(time_t) < sizeof(int64_t))
+                          ? std::numeric_limits<time_t>::min()
+                          : std::numeric_limits<int32_t>::min();
+      milliseconds = min_seconds * kMillisecondsPerSecond;
     } else {
-      milliseconds = (std::numeric_limits<time_t>::max() *
-                      kMillisecondsPerSecond) +
-                     kMillisecondsPerSecond - 1;
+      int64_t max_seconds = (sizeof(time_t) < sizeof(int64_t))
+                          ? std::numeric_limits<time_t>::max()
+                          : std::numeric_limits<int32_t>::max();
+      milliseconds = max_seconds * kMillisecondsPerSecond;
+      milliseconds += kMillisecondsPerSecond - 1;
     }
   } else {
     milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond;
   }
 
   return Time(milliseconds * kMicrosecondsPerMillisecond);
 }
 
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -86,16 +86,19 @@ class JavaScriptBase : public WrapperOwn
     }
     bool RecvHasInstance(const uint64_t& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
         return Answer::RecvHasInstance(ObjectId::deserialize(objId), v, rs, bp);
     }
     bool RecvObjectClassIs(const uint64_t& objId, const uint32_t& classValue,
                              bool* result) {
         return Answer::RecvObjectClassIs(ObjectId::deserialize(objId), classValue, result);
     }
+    bool RecvIsArray(const uint64_t& objId, ReturnStatus* rs, uint32_t* answer) {
+        return Answer::RecvIsArray(ObjectId::deserialize(objId), rs, answer);
+    }
     bool RecvClassName(const uint64_t& objId, nsCString* result) {
         return Answer::RecvClassName(ObjectId::deserialize(objId), result);
     }
     bool RecvGetPrototype(const uint64_t& objId, ReturnStatus* rs, ObjectOrNullVariant* result) {
         return Answer::RecvGetPrototype(ObjectId::deserialize(objId), rs, result);
     }
     bool RecvRegExpToShared(const uint64_t& objId, ReturnStatus* rs, nsString* source, uint32_t* flags) {
         return Answer::RecvRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
@@ -174,16 +177,20 @@ class JavaScriptBase : public WrapperOwn
     }
     bool SendHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
         return Base::SendHasInstance(objId.serialize(), v, rs, bp);
     }
     bool SendObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
                            bool* result) {
         return Base::SendObjectClassIs(objId.serialize(), classValue, result);
     }
+    bool SendIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* answer)
+    {
+        return Base::SendIsArray(objId.serialize(), rs, answer);
+    }
     bool SendClassName(const ObjectId& objId, nsCString* result) {
         return Base::SendClassName(objId.serialize(), result);
     }
     bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) {
         return Base::SendGetPrototype(objId.serialize(), rs, result);
     }
 
     bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs,
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -34,16 +34,17 @@ both:
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync Get(uint64_t objId, JSVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
     prio(high) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
+    prio(high) sync IsArray(uint64_t objId) returns (ReturnStatus rs, uint32_t ans);
     prio(high) sync ClassName(uint64_t objId) returns (nsCString name);
     prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
     prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
 
     prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);
     prio(high) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
     prio(high) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
 
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WrapperAnswer.h"
 #include "JavaScriptLogging.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "xpcprivate.h"
+#include "js/Class.h"
 #include "jsfriendapi.h"
 
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 // Note - Using AutoJSAPI (rather than AutoEntryScript) for a trap means
 // that we don't expect it to run script. For most of these traps that will only
@@ -517,16 +518,42 @@ WrapperAnswer::RecvObjectClassIs(const O
 
     LOG("%s.objectClassIs()", ReceiverObj(objId));
 
     *result = js::ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
     return true;
 }
 
 bool
+WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
+                           uint32_t* ans)
+{
+    *ans = uint32_t(IsArrayAnswer::NotArray);
+
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
+        return false;
+    jsapi.TakeOwnershipOfErrorReporting();
+    JSContext* cx = jsapi.cx();
+
+    RootedObject obj(cx, findObjectById(cx, objId));
+    if (!obj)
+        return fail(jsapi, rs);
+
+    LOG("%s.isArray()", ReceiverObj(objId));
+
+    IsArrayAnswer answer;
+    if (!JS::IsArray(cx, obj, &answer))
+        return fail(jsapi, rs);
+
+    *ans = uint32_t(answer);
+    return ok(rs);
+}
+
+bool
 WrapperAnswer::RecvClassName(const ObjectId& objId, nsCString* name)
 {
     AutoJSAPI jsapi;
     if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
         return false;
     jsapi.TakeOwnershipOfErrorReporting();
     JSContext* cx = jsapi.cx();
 
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -48,16 +48,17 @@ class WrapperAnswer : public virtual Jav
     bool RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs,
                           bool* result);
     bool RecvCallOrConstruct(const ObjectId& objId, InfallibleTArray<JSParam>&& argv,
                              const bool& construct, ReturnStatus* rs, JSVariant* result,
                              nsTArray<JSParam>* outparams);
     bool RecvHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp);
     bool RecvObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
                            bool* result);
+    bool RecvIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* ans);
     bool RecvClassName(const ObjectId& objId, nsCString* result);
     bool RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result);
     bool RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source, uint32_t* flags);
 
     bool RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
                              ReturnStatus* rs, nsTArray<JSIDVariant>* ids);
     bool RecvInstanceOf(const ObjectId& objId, const JSIID& iid,
                         ReturnStatus* rs, bool* instanceof);
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -122,16 +122,18 @@ class CPOWProxyHandler : public BaseProx
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
     virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
                                JSContext* cx) const override;
+    virtual bool isArray(JSContext* cx, HandleObject obj,
+                         IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
 
@@ -736,16 +738,43 @@ WrapperOwner::objectClassIs(JSContext* c
     if (!SendObjectClassIs(objId, classValue, &result))
         return false;
 
     LOG_STACK();
 
     return result;
 }
 
+bool
+CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
+                          IsArrayAnswer* answer) const
+{
+    FORWARD(isArray, (cx, proxy, answer));
+}
+
+bool
+WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
+{
+    ObjectId objId = idOf(proxy);
+
+    uint32_t ans;
+    ReturnStatus status;
+    if (!SendIsArray(objId, &status, &ans))
+        return ipcfail(cx);
+
+    LOG_STACK();
+
+    *answer = IsArrayAnswer(ans);
+    MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
+               *answer == IsArrayAnswer::NotArray ||
+               *answer == IsArrayAnswer::RevokedProxy);
+
+    return ok(cx, status);
+}
+
 const char*
 CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
 {
     WrapperOwner* parent = OwnerOf(proxy);
     if (!parent->active())
         return "<dead CPOW>";
     return parent->className(cx, proxy);
 }
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -50,16 +50,17 @@ class WrapperOwner : public virtual Java
     // SpiderMonkey extensions.
     bool getPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                                JS::MutableHandle<JSPropertyDescriptor> desc);
     bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
     bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::HandleObject proxy,
                                       JS::AutoIdVector& props);
     bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
     bool objectClassIs(JSContext* cx, JS::HandleObject obj, js::ESClassValue classValue);
+    bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
     const char* className(JSContext* cx, JS::HandleObject proxy);
     bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
 
     bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
 
     nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
 
     bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
@@ -135,16 +136,18 @@ class WrapperOwner : public virtual Java
                                   bool* result) = 0;
     virtual bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
                                      const bool& construct, ReturnStatus* rs, JSVariant* result,
                                      nsTArray<JSParam>* outparams) = 0;
     virtual bool SendHasInstance(const ObjectId& objId, const JSVariant& v,
                                  ReturnStatus* rs, bool* bp) = 0;
     virtual bool SendObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
                                    bool* result) = 0;
+    virtual bool SendIsArray(const ObjectId& objId, ReturnStatus* rs,
+                             uint32_t* answer) = 0;
     virtual bool SendClassName(const ObjectId& objId, nsCString* result) = 0;
     virtual bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) = 0;
     virtual bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source,
                                     uint32_t* flags) = 0;
 
     virtual bool SendGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
                                      ReturnStatus* rs, nsTArray<JSIDVariant>* ids) = 0;
     virtual bool SendInstanceOf(const ObjectId& objId, const JSIID& iid,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -41,16 +41,47 @@ extern JS_FRIEND_DATA(const js::Class* c
 } // namespace js
 
 namespace JS {
 
 template <typename T>
 class AutoVectorRooter;
 typedef AutoVectorRooter<jsid> AutoIdVector;
 
+// The answer to a successful query as to whether an object is an Array per
+// ES6's internal |IsArray| operation (as exposed by |Array.isArray|).
+enum class IsArrayAnswer
+{
+    Array,
+    NotArray,
+    RevokedProxy
+};
+
+// ES6 7.2.2.
+//
+// Returns false on failure, otherwise returns true and sets |*isArray|
+// indicating whether the object passes ECMAScript's IsArray test.  This is the
+// same test performed by |Array.isArray|.
+//
+// This is NOT the same as asking whether |obj| is an Array or a wrapper around
+// one.  If |obj| is a proxy created by |Proxy.revocable()| and has been
+// revoked, or if |obj| is a proxy whose target (at any number of hops) is a
+// revoked proxy, this method throws a TypeError and returns false.
+extern JS_PUBLIC_API(bool)
+IsArray(JSContext* cx, HandleObject obj, bool* isArray);
+
+// Identical to IsArray above, but the nature of the object (if successfully
+// determined) is communicated via |*answer|.  In particular this method
+// returns true and sets |*answer = IsArrayAnswer::RevokedProxy| when called on
+// a revoked proxy.
+//
+// Most users will want the overload above, not this one.
+extern JS_PUBLIC_API(bool)
+IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer);
+
 /*
  * Per ES6, the [[DefineOwnProperty]] internal method has three different
  * possible outcomes:
  *
  * -   It can throw an exception (which we indicate by returning false).
  *
  * -   It can return true, indicating unvarnished success.
  *
@@ -795,21 +826,17 @@ Valueify(const JSClass* c)
 
 /*
  * Enumeration describing possible values of the [[Class]] internal property
  * value of objects.
  */
 enum ESClassValue {
     ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
     ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
-    ESClass_Date, ESClass_Set, ESClass_Map,
-
-    // Special snowflake for the ES6 IsArray method.
-    // Please don't use it without calling that function.
-    ESClass_IsArray
+    ESClass_Date, ESClass_Set, ESClass_Map
 };
 
 /*
  * Return whether the given object has the given [[Class]] internal property
  * value. Beware, this query says nothing about the js::Class of the JSObject
  * so the caller must not assume anything about obj's representation (e.g., obj
  * may be a proxy).
  */
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -318,16 +318,17 @@ class JS_FRIEND_API(BaseProxyHandler)
                                        MutableHandle<JSPropertyDescriptor> desc) const;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const;
+    virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
@@ -409,16 +410,18 @@ class JS_FRIEND_API(DirectProxyHandler) 
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext* cx) const override;
+    virtual bool isArray(JSContext* cx, HandleObject proxy,
+                         JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
                                  RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override;
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -176,16 +176,55 @@ namespace js {
 namespace oom {
 static inline bool IsSimulatedOOMAllocation() { return false; }
 static inline bool ShouldFailWithOOM() { return false; }
 } /* namespace oom */
 } /* namespace js */
 
 # endif /* DEBUG || JS_OOM_BREAKPOINT */
 
+namespace js {
+
+MOZ_NORETURN MOZ_COLD void
+CrashAtUnhandlableOOM(const char* reason);
+
+/* Disable OOM testing in sections which are not OOM safe. */
+struct MOZ_RAII AutoEnterOOMUnsafeRegion
+{
+    void crash(const char* reason) {
+        CrashAtUnhandlableOOM(reason);
+    }
+
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+    AutoEnterOOMUnsafeRegion()
+      : oomEnabled_(OOM_maxAllocations != UINT32_MAX), oomAfter_(0)
+    {
+        if (oomEnabled_) {
+            oomAfter_ = int64_t(OOM_maxAllocations) - OOM_counter;
+            OOM_maxAllocations = UINT32_MAX;
+        }
+    }
+
+    ~AutoEnterOOMUnsafeRegion() {
+        MOZ_ASSERT(OOM_maxAllocations == UINT32_MAX);
+        if (oomEnabled_) {
+            int64_t maxAllocations = OOM_counter + oomAfter_;
+            MOZ_ASSERT(maxAllocations >= 0 && maxAllocations < UINT32_MAX);
+            OOM_maxAllocations = uint32_t(maxAllocations);
+        }
+    }
+
+  private:
+    bool oomEnabled_;
+    int64_t oomAfter_;
+#endif
+};
+
+} /* namespace js */
+
 static inline void* js_malloc(size_t bytes)
 {
     JS_OOM_POSSIBLY_FAIL();
     return malloc(bytes);
 }
 
 static inline void* js_calloc(size_t bytes)
 {
--- a/js/src/builtin/Date.js
+++ b/js/src/builtin/Date.js
@@ -1,19 +1,83 @@
 /* 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/. */
 
 /*global intl_DateTimeFormat: false, */
 
 
+// This cache, once primed, has these properties:
+//
+//   runtimeDefaultLocale:
+//     Locale information provided by the embedding, guiding SpiderMonkey's
+//     selection of a default locale.  See RuntimeDefaultLocale(), whose
+//     value controls the value returned by DefaultLocale() that's what's
+//     *actually* used.
+//   localTZA:
+//     The local time zone's adjustment from UTC.  See LocalTZA().
+//   formatters:
+//     A Record storing formatters consistent with the above
+//     runtimeDefaultLocale/localTZA values, for use with the appropriate
+//     ES6 toLocale*String Date method when called with its first two
+//     arguments having the value |undefined|.
+//
+// The "formatters" Record has (some subset of) these properties, as determined
+// by all values of the first argument passed to |GetCachedFormat|:
+//
+//   dateTimeFormat: for Date's toLocaleString operation
+//   dateFormat: for Date's toLocaleDateString operation
+//   timeFormat: for Date's toLocaleTimeString operation
+//
+// Using this cache, then, requires 1) verifying the current
+// runtimeDefaultLocale/localTZA are consistent with cached values, then
+// 2) seeing if the desired formatter is cached and returning it if so, or else
+// 3) create the desired formatter and store and return it.
 var dateTimeFormatCache = new Record();
 
 
 /**
+ * Get a cached DateTimeFormat formatter object, created like so:
+ *
+ *   var opts = ToDateTimeOptions(undefined, required, defaults);
+ *   return new Intl.DateTimeFormat(undefined, opts);
+ *
+ * |format| must be a key from the "formatters" Record described above.
+ */
+function GetCachedFormat(format, required, defaults) {
+    assert(format === "dateTimeFormat" ||
+           format === "dateFormat" ||
+           format === "timeFormat",
+           "unexpected format key: please update the comment by " +
+           "dateTimeFormatCache");
+
+    var runtimeDefaultLocale = RuntimeDefaultLocale();
+    var localTZA = LocalTZA();
+
+    var formatters;
+    if (dateTimeFormatCache.runtimeDefaultLocale !== runtimeDefaultLocale ||
+        dateTimeFormatCache.localTZA !== localTZA)
+    {
+        formatters = dateTimeFormatCache.formatters = new Record();
+        dateTimeFormatCache.runtimeDefaultLocale = runtimeDefaultLocale;
+        dateTimeFormatCache.localTZA = localTZA;
+    } else {
+        formatters = dateTimeFormatCache.formatters;
+    }
+
+    var fmt = formatters[format];
+    if (fmt === undefined) {
+        var options = ToDateTimeOptions(undefined, required, defaults);
+        fmt = formatters[format] = intl_DateTimeFormat(undefined, options);
+    }
+
+    return fmt;
+}
+
+/**
  * Format this Date object into a date and time string, using the locale and
  * formatting options provided.
  *
  * Spec: ECMAScript Language Specification, 5.1 edition, 15.9.5.5.
  * Spec: ECMAScript Internationalization API Specification, 13.3.1.
  */
 function Date_toLocaleString() {
     // Steps 1-2.  Note that valueOf enforces "this time value" restrictions.
@@ -25,21 +89,17 @@ function Date_toLocaleString() {
     var locales = arguments.length > 0 ? arguments[0] : undefined;
     var options = arguments.length > 1 ? arguments[1] : undefined;
 
     // Step 5-6.
     var dateTimeFormat;
     if (locales === undefined && options === undefined) {
         // This cache only optimizes for the old ES5 toLocaleString without
         // locales and options.
-        if (dateTimeFormatCache.dateTimeFormat === undefined) {
-            options = ToDateTimeOptions(options, "any", "all");
-            dateTimeFormatCache.dateTimeFormat = intl_DateTimeFormat(locales, options);
-        }
-        dateTimeFormat = dateTimeFormatCache.dateTimeFormat;
+        dateTimeFormat = GetCachedFormat("dateTimeFormat", "any", "all");
     } else {
         options = ToDateTimeOptions(options, "any", "all");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
     return intl_FormatDateTime(dateTimeFormat, x);
 }
@@ -62,21 +122,17 @@ function Date_toLocaleDateString() {
     var locales = arguments.length > 0 ? arguments[0] : undefined;
     var options = arguments.length > 1 ? arguments[1] : undefined;
 
     // Step 5-6.
     var dateTimeFormat;
     if (locales === undefined && options === undefined) {
         // This cache only optimizes for the old ES5 toLocaleDateString without
         // locales and options.
-        if (dateTimeFormatCache.dateFormat === undefined) {
-            options = ToDateTimeOptions(options, "date", "date");
-            dateTimeFormatCache.dateFormat = intl_DateTimeFormat(locales, options);
-        }
-        dateTimeFormat = dateTimeFormatCache.dateFormat;
+        dateTimeFormat = GetCachedFormat("dateFormat", "date", "date");
     } else {
         options = ToDateTimeOptions(options, "date", "date");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
     return intl_FormatDateTime(dateTimeFormat, x);
 }
@@ -99,21 +155,17 @@ function Date_toLocaleTimeString() {
     var locales = arguments.length > 0 ? arguments[0] : undefined;
     var options = arguments.length > 1 ? arguments[1] : undefined;
 
     // Step 5-6.
     var dateTimeFormat;
     if (locales === undefined && options === undefined) {
         // This cache only optimizes for the old ES5 toLocaleTimeString without
         // locales and options.
-        if (dateTimeFormatCache.timeFormat === undefined) {
-            options = ToDateTimeOptions(options, "time", "time");
-            dateTimeFormatCache.timeFormat = intl_DateTimeFormat(locales, options);
-        }
-        dateTimeFormat = dateTimeFormatCache.timeFormat;
+        dateTimeFormat = GetCachedFormat("timeFormat", "time", "time");
     } else {
         options = ToDateTimeOptions(options, "time", "time");
         dateTimeFormat = intl_DateTimeFormat(locales, options);
     }
 
     // Step 7.
     return intl_FormatDateTime(dateTimeFormat, x);
 }
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -775,19 +775,19 @@ ReplaceLane(JSContext* cx, unsigned argc
     CallArgs args = CallArgsFromVp(argc, vp);
     // Only the first and second arguments are mandatory
     if (args.length() < 2 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     Elem* vec = TypedObjectMemory<Elem*>(args[0]);
     Elem result[V::lanes];
 
-    if (!args[1].isInt32())
+    int32_t lanearg;
+    if (!args[1].isNumber() || !NumberIsInt32(args[1].toNumber(), &lanearg))
         return ErrorBadArgs(cx);
-    int32_t lanearg = args[1].toInt32();
     if (lanearg < 0 || uint32_t(lanearg) >= V::lanes)
         return ErrorBadArgs(cx);
     uint32_t lane = uint32_t(lanearg);
 
     Elem value;
     if (!V::toType(cx, args.get(2), &value))
         return false;
 
@@ -803,19 +803,19 @@ Swizzle(JSContext* cx, unsigned argc, Va
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != (V::lanes + 1) || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     uint32_t lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
-        if (!args[i + 1].isInt32())
+        int32_t lane;
+        if (!args[i + 1].isNumber() || !NumberIsInt32(args[i + 1].toNumber(), &lane))
             return ErrorBadArgs(cx);
-        int32_t lane = args[i + 1].toInt32();
         if (lane < 0 || uint32_t(lane) >= V::lanes)
             return ErrorBadArgs(cx);
         lanes[i] = uint32_t(lane);
     }
 
     Elem* val = TypedObjectMemory<Elem*>(args[0]);
 
     Elem result[V::lanes];
@@ -832,19 +832,19 @@ Shuffle(JSContext* cx, unsigned argc, Va
     typedef typename V::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != (V::lanes + 2) || !IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]))
         return ErrorBadArgs(cx);
 
     uint32_t lanes[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++) {
-        if (!args[i + 2].isInt32())
+        int32_t lane;
+        if (!args[i + 2].isNumber() || !NumberIsInt32(args[i + 2].toNumber(), &lane))
             return ErrorBadArgs(cx);
-        int32_t lane = args[i + 2].toInt32();
         if (lane < 0 || uint32_t(lane) >= (2 * V::lanes))
             return ErrorBadArgs(cx);
         lanes[i] = uint32_t(lane);
     }
 
     Elem* lhs = TypedObjectMemory<Elem*>(args[0]);
     Elem* rhs = TypedObjectMemory<Elem*>(args[1]);
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1379,50 +1379,52 @@ DisplayName(JSContext* cx, unsigned argc
     JSString* str = fun->displayAtom();
     args.rval().setString(str ? str : cx->runtime()->emptyString);
     return true;
 }
 
 static JSObject*
 ShellObjectMetadataCallback(JSContext* cx, JSObject*)
 {
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+
     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
-        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
+        oomUnsafe.crash("ShellObjectMetadataCallback");
 
     RootedObject stack(cx, NewDenseEmptyArray(cx));
     if (!stack)
-        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
+        oomUnsafe.crash("ShellObjectMetadataCallback");
 
     static int createdIndex = 0;
     createdIndex++;
 
     if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
                            JS_STUBGETTER, JS_STUBSETTER))
     {
-        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
+        oomUnsafe.crash("ShellObjectMetadataCallback");
     }
 
     if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
                            JS_STUBGETTER, JS_STUBSETTER))
     {
-        CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
+        oomUnsafe.crash("ShellObjectMetadataCallback");
     }
 
     int stackIndex = 0;
     RootedId id(cx);
     RootedValue callee(cx);
     for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
         if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
             id = INT_TO_JSID(stackIndex);
             RootedObject callee(cx, iter.callee(cx));
             if (!JS_DefinePropertyById(cx, stack, id, callee, 0,
                                        JS_STUBGETTER, JS_STUBSETTER))
             {
-                CrashAtUnhandlableOOM("ShellObjectMetadataCallback");
+                oomUnsafe.crash("ShellObjectMetadataCallback");
             }
             stackIndex++;
         }
     }
 
     return obj;
 }
 
@@ -2457,16 +2459,25 @@ ByteSize(JSContext* cx, unsigned argc, V
             args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
         else
             args.rval().setUndefined();
     }
     return true;
 }
 
 static bool
+ImmutablePrototypesEnabled(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    args.rval().setBoolean(JS_ImmutablePrototypesEnabled());
+    return true;
+}
+
+static bool
 SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject()) {
         JS_ReportError(cx, "setImmutablePrototype: object expected");
         return false;
     }
 
@@ -2787,16 +2798,25 @@ GetLcovInfo(JSContext* cx, unsigned argc
 
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
+static bool
+EnableNoSuchMethod(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    cx->runtime()->options().setNoSuchMethod(true);
+    args.rval().setUndefined();
+    return true;
+}
+
 #ifdef DEBUG
 static bool
 SetRNGState(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "SetRNGState", 1))
         return false;
 
@@ -3199,16 +3219,21 @@ gc::ZealModeHelpText),
 "    options.locals - show local variables in each frame\n"
 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
 
     JS_FN_HELP("byteSize", ByteSize, 1, 0,
 "byteSize(value)",
 "  Return the size in bytes occupied by |value|, or |undefined| if value\n"
 "  is not allocated in memory.\n"),
 
+    JS_FN_HELP("immutablePrototypesEnabled", ImmutablePrototypesEnabled, 0, 0,
+"immutablePrototypesEnabled()",
+"  Returns true if immutable-prototype behavior (triggered by setImmutablePrototype)\n"
+"  is enabled, such that modifying an immutable prototype will fail."),
+
     JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
 "setImmutablePrototype(obj)",
 "  Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
 "  change it will fail.  Return true if obj's [[Prototype]] was successfully made\n"
 "  immutable (or if it already was immutable), false otherwise.  Throws in case\n"
 "  of internal error, or if the operation doesn't even make sense (for example,\n"
 "  because the object is a revoked proxy)."),
 
@@ -3253,16 +3278,20 @@ gc::ZealModeHelpText),
 "  On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
 "  flags is available by calling this function with \"help\" as the flag's name"),
 
     JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
 "getLcovInfo(global)",
 "  Generate LCOV tracefile for the given compartment.  If no global are provided then\n"
 "  the current global is used as the default one.\n"),
 
+    JS_FN_HELP("enableNoSuchMethod", EnableNoSuchMethod, 0, 0,
+"enableNoSuchMethod()",
+"  Enables the deprecated, non-standard __noSuchMethod__ feature.\n"),
+
 #ifdef DEBUG
     JS_FN_HELP("setRNGState", SetRNGState, 1, 0,
 "setRNGState(seed)",
 "  Set this compartment's RNG state.\n"),
 #endif
 
     JS_FS_HELP_END
 };
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1515,18 +1515,21 @@ OutlineTypedObject::attach(JSContext* cx
     if (buffer.forInlineTypedObject()) {
         InlineTypedObject& realOwner = buffer.firstView()->as<InlineTypedObject>();
         attach(cx, realOwner, offset);
         return;
     }
 
     buffer.setHasTypedObjectViews();
 
-    if (!buffer.addView(cx, this))
-        CrashAtUnhandlableOOM("TypedObject::attach");
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        if (!buffer.addView(cx, this))
+            oomUnsafe.crash("TypedObject::attach");
+    }
 
     setOwnerAndData(&buffer, buffer.dataPointer() + offset);
 }
 
 void
 OutlineTypedObject::attach(JSContext* cx, TypedObject& typedObj, int32_t offset)
 {
     MOZ_ASSERT(!isAttached());
@@ -3021,18 +3024,19 @@ TraceListVisitor::visitReference(Referen
     VectorType* offsets;
     switch (descr.type()) {
       case ReferenceTypeDescr::TYPE_ANY: offsets = &valueOffsets; break;
       case ReferenceTypeDescr::TYPE_OBJECT: offsets = &objectOffsets; break;
       case ReferenceTypeDescr::TYPE_STRING: offsets = &stringOffsets; break;
       default: MOZ_CRASH("Invalid kind");
     }
 
+    AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!offsets->append((uintptr_t) mem))
-        CrashAtUnhandlableOOM("TraceListVisitor::visitReference");
+        oomUnsafe.crash("TraceListVisitor::visitReference");
 }
 
 bool
 TraceListVisitor::fillList(Vector<int32_t>& entries)
 {
     return entries.appendAll(stringOffsets) &&
            entries.append(-1) &&
            entries.appendAll(objectOffsets) &&
--- a/js/src/ds/Fifo.h
+++ b/js/src/ds/Fifo.h
@@ -130,18 +130,19 @@ class Fifo
     bool popFront() {
         MOZ_ASSERT(!empty());
         T t(mozilla::Move(front()));
         front_.popBack();
         if (!fixup()) {
             // Attempt to remain in a valid state by reinserting the element
             // back at the front. If we can't remain in a valid state in the
             // face of OOMs, crash.
+            AutoEnterOOMUnsafeRegion oomUnsafe;
             if (!front_.append(mozilla::Move(t)))
-                CrashAtUnhandlableOOM("js::Fifo::popFront");
+                oomUnsafe.crash("js::Fifo::popFront");
             return false;
         }
         return true;
     }
 
     // Clear all elements from the queue.
     void clear() {
         front_.clear();
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -272,19 +272,20 @@ class LifoAlloc
     MOZ_ALWAYS_INLINE
     void* alloc(size_t n) {
         JS_OOM_POSSIBLY_FAIL();
         return allocImpl(n);
     }
 
     MOZ_ALWAYS_INLINE
     void* allocInfallible(size_t n) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (void* result = allocImpl(n))
             return result;
-        CrashAtUnhandlableOOM("LifoAlloc::allocInfallible");
+        oomUnsafe.crash("LifoAlloc::allocInfallible");
         return nullptr;
     }
 
     // Ensures that enough space exists to satisfy N bytes worth of
     // allocation requests, not necessarily contiguous. Note that this does
     // not guarantee a successful single allocation of N bytes.
     MOZ_ALWAYS_INLINE
     bool ensureUnusedApproximate(size_t n) {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1783,18 +1783,18 @@ GCMarker::enterWeakMarkingMode()
     if (linearWeakMarkingDisabled_)
         return;
 
     // During weak marking mode, we maintain a table mapping weak keys to
     // entries in known-live weakmaps.
     if (weakMapAction() == ExpandWeakMaps) {
         tag_ = TracerKindTag::WeakMarking;
 
-        for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) {
-            for (WeakMapBase* m = c->gcWeakMapList; m; m = m->next) {
+        for (GCZoneGroupIter zone(runtime()); !zone.done(); zone.next()) {
+            for (WeakMapBase* m = zone->gcWeakMapList; m; m = m->next) {
                 if (m->marked)
                     m->markEphemeronEntries(this);
             }
         }
     }
 }
 
 void
@@ -2055,19 +2055,20 @@ js::TenuringTracer::moveToTenured(JSObje
 
     AllocKind dstKind = src->allocKindForTenure(nursery());
     Zone* zone = src->zone();
 
     TenuredCell* t = zone->arenas.allocateFromFreeList(dstKind, Arena::thingSize(dstKind));
     if (!t) {
         zone->arenas.checkEmptyFreeList(dstKind);
         AutoMaybeStartBackgroundAllocation maybeStartBackgroundAllocation;
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         t = zone->arenas.allocateFromArena(zone, dstKind, maybeStartBackgroundAllocation);
         if (!t)
-            CrashAtUnhandlableOOM("Failed to allocate object while tenuring.");
+            oomUnsafe.crash("Failed to allocate object while tenuring.");
     }
     JSObject* dst = reinterpret_cast<JSObject*>(t);
     tenuredSize += moveObjectToTenured(dst, src, dstKind);
 
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
     insertIntoFixupList(overlay);
 
@@ -2207,19 +2208,24 @@ js::TenuringTracer::moveSlotsToTenured(N
 
     if (!nursery().isInside(src->slots_)) {
         nursery().removeMallocedBuffer(src->slots_);
         return 0;
     }
 
     Zone* zone = src->zone();
     size_t count = src->numDynamicSlots();
-    dst->slots_ = zone->pod_malloc<HeapSlot>(count);
-    if (!dst->slots_)
-        CrashAtUnhandlableOOM("Failed to allocate slots while tenuring.");
+
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        dst->slots_ = zone->pod_malloc<HeapSlot>(count);
+        if (!dst->slots_)
+            oomUnsafe.crash("Failed to allocate slots while tenuring.");
+    }
+
     PodCopy(dst->slots_, src->slots_, count);
     nursery().setSlotsForwardingPointer(src->slots_, dst->slots_, count);
     return count * sizeof(HeapSlot);
 }
 
 size_t
 js::TenuringTracer::moveElementsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
 {
@@ -2244,19 +2250,24 @@ js::TenuringTracer::moveElementsToTenure
         dst->as<ArrayObject>().setFixedElements();
         dstHeader = dst->as<ArrayObject>().getElementsHeader();
         js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
         nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots);
         return nslots * sizeof(HeapSlot);
     }
 
     MOZ_ASSERT(nslots >= 2);
-    dstHeader = reinterpret_cast<ObjectElements*>(zone->pod_malloc<HeapSlot>(nslots));
-    if (!dstHeader)
-        CrashAtUnhandlableOOM("Failed to allocate elements while tenuring.");
+
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        dstHeader = reinterpret_cast<ObjectElements*>(zone->pod_malloc<HeapSlot>(nslots));
+        if (!dstHeader)
+            oomUnsafe.crash("Failed to allocate elements while tenuring.");
+    }
+
     js_memcpy(dstHeader, srcHeader, nslots * sizeof(HeapSlot));
     nursery().setElementsForwardingPointer(srcHeader, dstHeader, nslots);
     dst->elements_ = dstHeader->elements();
     return nslots * sizeof(HeapSlot);
 }
 
 
 /*** IsMarked / IsAboutToBeFinalized **************************************************************/
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -310,24 +310,25 @@ Nursery::setForwardingPointer(void* oldD
     // Bug 1196210: If a zero-capacity header lands in the last 2 words of the
     // jemalloc chunk abutting the start of the nursery, the (invalid) newData
     // pointer will appear to be "inside" the nursery.
     MOZ_ASSERT(!isInside(newData) || uintptr_t(newData) == heapStart_);
 
     if (direct) {
         *reinterpret_cast<void**>(oldData) = newData;
     } else {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!forwardedBuffers.initialized() && !forwardedBuffers.init())
-            CrashAtUnhandlableOOM("Nursery::setForwardingPointer");
+            oomUnsafe.crash("Nursery::setForwardingPointer");
 #ifdef DEBUG
         if (ForwardedBufferMap::Ptr p = forwardedBuffers.lookup(oldData))
             MOZ_ASSERT(p->value() == newData);
 #endif
         if (!forwardedBuffers.put(oldData, newData))
-            CrashAtUnhandlableOOM("Nursery::setForwardingPointer");
+            oomUnsafe.crash("Nursery::setForwardingPointer");
     }
 }
 
 void
 Nursery::setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint32_t nslots)
 {
     // Slot arrays always have enough space for a forwarding pointer, since the
     // number of slots is never zero.
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -102,18 +102,19 @@ class StoreBuffer
             }
             stores_.remove(v);
         }
 
         /* Move any buffered stores to the canonical store set. */
         void sinkStore(StoreBuffer* owner) {
             MOZ_ASSERT(stores_.initialized());
             if (last_) {
+                AutoEnterOOMUnsafeRegion oomUnsafe;
                 if (!stores_.put(last_))
-                    CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::put.");
+                    oomUnsafe.crash("Failed to allocate for MonoTypeBuffer::put.");
             }
             last_ = T();
 
             if (MOZ_UNLIKELY(stores_.count() > MaxEntries))
                 owner->setAboutToOverflow();
         }
 
         bool has(StoreBuffer* owner, const T& v) {
@@ -162,25 +163,26 @@ class StoreBuffer
 
         template <typename T>
         void put(StoreBuffer* owner, const T& t) {
             MOZ_ASSERT(storage_);
 
             /* Ensure T is derived from BufferableRef. */
             (void)static_cast<const BufferableRef*>(&t);
 
+            AutoEnterOOMUnsafeRegion oomUnsafe;
             unsigned size = sizeof(T);
             unsigned* sizep = storage_->pod_malloc<unsigned>();
             if (!sizep)
-                CrashAtUnhandlableOOM("Failed to allocate for GenericBuffer::put.");
+                oomUnsafe.crash("Failed to allocate for GenericBuffer::put.");
             *sizep = size;
 
             T* tp = storage_->new_<T>(t);
             if (!tp)
-                CrashAtUnhandlableOOM("Failed to allocate for GenericBuffer::put.");
+                oomUnsafe.crash("Failed to allocate for GenericBuffer::put.");
 
             if (isAboutToOverflow())
                 owner->setAboutToOverflow();
         }
 
         size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
             return storage_ ? storage_->sizeOfIncludingThis(mallocSizeOf) : 0;
         }
@@ -440,24 +442,16 @@ class StoreBuffer
     void putGeneric(const T& t) { putFromAnyThread(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     template <typename Key>
     void putCallback(void (*callback)(JSTracer* trc, Key* key, void* data), Key* key, void* data) {
         putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data));
     }
 
-    void assertHasCellEdge(Cell** cellp) {
-        CellPtrEdge cpe(cellp);
-
-        MOZ_ASSERT(bufferCell.has(this, CellPtrEdge(cellp)) ||
-                   !CellPtrEdge(cellp).maybeInRememberedSet(nursery_));
-
-    }
-
     void assertHasValueEdge(JS::Value* vp) {
         MOZ_ASSERT(bufferVal.has(this, ValueEdge(vp)) ||
                    !ValueEdge(vp).maybeInRememberedSet(nursery_));
     }
 
     void setShouldCancelIonCompilations() {
         cancelIonCompilations_ = true;
     }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -22,16 +22,17 @@ using namespace js::gc;
 
 Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
 
 JS::Zone::Zone(JSRuntime* rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     debuggers(nullptr),
     arenas(rt),
     types(this),
+    gcWeakMapList(nullptr),
     compartments(),
     gcGrayRoots(),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
     gcDelayBytes(0),
     data(nullptr),
     isSystem(false),
@@ -199,16 +200,23 @@ Zone::sweepBreakpoints(FreeOp* fop)
                 if (dying)
                     bp->destroy(fop);
             }
         }
     }
 }
 
 void
+Zone::sweepWeakMaps()
+{
+    /* Finalize unreachable (key,value) pairs in all weak maps. */
+    WeakMapBase::sweepZone(this);
+}
+
+void
 Zone::discardJitCode(FreeOp* fop)
 {
     if (!jitZone())
         return;
 
     if (isPreservingCode()) {
         PurgeJITCaches(this);
     } else {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -237,41 +237,46 @@ struct Zone : public JS::shadow::Zone,
 
   private:
     DebuggerVector* debuggers;
 
     using LogTenurePromotionQueue = js::Vector<JSObject*, 0, js::SystemAllocPolicy>;
     LogTenurePromotionQueue awaitingTenureLogging;
 
     void sweepBreakpoints(js::FreeOp* fop);
+    void sweepWeakMaps();
     void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
 
     js::jit::JitZone* createJitZone(JSContext* cx);
 
     bool isQueuedForBackgroundSweep() {
         return isOnList();
     }
 
   public:
     bool hasDebuggers() const { return debuggers && debuggers->length(); }
     DebuggerVector* getDebuggers() const { return debuggers; }
     DebuggerVector* getOrCreateDebuggers(JSContext* cx);
 
     void enqueueForPromotionToTenuredLogging(JSObject& obj) {
         MOZ_ASSERT(hasDebuggers());
         MOZ_ASSERT(!IsInsideNursery(&obj));
+        js::AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!awaitingTenureLogging.append(&obj))
-            js::CrashAtUnhandlableOOM("Zone::enqueueForPromotionToTenuredLogging");
+            oomUnsafe.crash("Zone::enqueueForPromotionToTenuredLogging");
     }
     void logPromotionsToTenured();
 
     js::gc::ArenaLists arenas;
 
     js::TypeZone types;
 
+    /* Linked list of live weakmaps in this zone. */
+    js::WeakMapBase* gcWeakMapList;
+
     // The set of compartments in this zone.
     typedef js::Vector<JSCompartment*, 1, js::SystemAllocPolicy> CompartmentVector;
     CompartmentVector compartments;
 
     // This compartment's gray roots.
     typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
     GrayRootVector gcGrayRoots;
 
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -982,18 +982,22 @@ NativeRegExpMacroAssembler::PopRegister(
 void
 NativeRegExpMacroAssembler::PushBacktrack(Label* label)
 {
     JitSpew(SPEW_PREFIX "PushBacktrack");
 
     CodeOffsetLabel patchOffset = masm.movWithPatch(ImmPtr(nullptr), temp0);
 
     MOZ_ASSERT(!label->bound());
-    if (!labelPatches.append(LabelPatch(label, patchOffset)))
-        CrashAtUnhandlableOOM("NativeRegExpMacroAssembler::PushBacktrack");
+
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        if (!labelPatches.append(LabelPatch(label, patchOffset)))
+            oomUnsafe.crash("NativeRegExpMacroAssembler::PushBacktrack");
+    }
 
     PushBacktrack(temp0);
     CheckBacktrackStackLimit();
 }
 
 void
 NativeRegExpMacroAssembler::BindBacktrack(Label* label)
 {
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1504,18 +1504,19 @@ class irregexp::RegExpCompiler
     }
 
     RegExpCode Assemble(JSContext* cx,
                         RegExpMacroAssembler* assembler,
                         RegExpNode* start,
                         int capture_count);
 
     inline void AddWork(RegExpNode* node) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!work_list_.append(node))
-            CrashAtUnhandlableOOM("AddWork");
+            oomUnsafe.crash("AddWork");
     }
 
     static const int kImplementationOffset = 0;
     static const int kNumberOfRegistersOffset = 0;
     static const int kCodeOffset = 1;
 
     RegExpMacroAssembler* macro_assembler() { return macro_assembler_; }
     EndNode* accept() { return accept_; }
@@ -2389,19 +2390,23 @@ BoyerMooreLookahead::EmitSkipInstruction
             masm->CheckCharacter(single_character, &cont);
         }
         masm->AdvanceCurrentPosition(lookahead_width);
         masm->JumpOrBacktrack(&again);
         masm->Bind(&cont);
         return true;
     }
 
-    uint8_t* boolean_skip_table = static_cast<uint8_t*>(js_malloc(kSize));
-    if (!boolean_skip_table || !masm->shared->addTable(boolean_skip_table))
-        CrashAtUnhandlableOOM("Table malloc");
+    uint8_t* boolean_skip_table;
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        boolean_skip_table = static_cast<uint8_t*>(js_malloc(kSize));
+        if (!boolean_skip_table || !masm->shared->addTable(boolean_skip_table))
+            oomUnsafe.crash("Table malloc");
+    }
 
     int skip_distance = GetSkipTable(min_lookahead, max_lookahead, boolean_skip_table);
     MOZ_ASSERT(skip_distance != 0);
 
     jit::Label cont, again;
     masm->Bind(&again);
     masm->LoadCurrentCharacter(max_lookahead, &cont, true);
     masm->CheckBitInTable(boolean_skip_table, &cont);
@@ -3096,19 +3101,23 @@ EmitUseLookupTable(RegExpMacroAssembler*
         }
         bit ^= 1;
     }
     for (int i = j; i < kSize; i++) {
         templ[i] = bit;
     }
 
     // TODO(erikcorry): Cache these.
-    uint8_t* ba = static_cast<uint8_t*>(js_malloc(kSize));
-    if (!ba || !masm->shared->addTable(ba))
-        CrashAtUnhandlableOOM("Table malloc");
+    uint8_t* ba;
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        ba = static_cast<uint8_t*>(js_malloc(kSize));
+        if (!ba || !masm->shared->addTable(ba))
+            oomUnsafe.crash("Table malloc");
+    }
 
     for (int i = 0; i < kSize; i++)
         ba[i] = templ[i];
 
     masm->CheckBitInTable(ba, on_bit_set);
     if (on_bit_clear != fall_through)
         masm->JumpOrBacktrack(on_bit_clear);
 }
--- a/js/src/irregexp/RegExpMacroAssembler.cpp
+++ b/js/src/irregexp/RegExpMacroAssembler.cpp
@@ -518,17 +518,19 @@ InterpretedRegExpMacroAssembler::Emit8(u
         Expand();
     *reinterpret_cast<unsigned char*>(buffer_ + pc_) = word;
     pc_ += 1;
 }
 
 void
 InterpretedRegExpMacroAssembler::Expand()
 {
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+
     int newLength = Max(100, length_ * 2);
     if (newLength < length_ + 4)
-        CrashAtUnhandlableOOM("InterpretedRegExpMacroAssembler::Expand");
+        oomUnsafe.crash("InterpretedRegExpMacroAssembler::Expand");
 
     buffer_ = (uint8_t*) js_realloc(buffer_, newLength);
     if (!buffer_)
-        CrashAtUnhandlableOOM("InterpretedRegExpMacroAssembler::Expand");
+        oomUnsafe.crash("InterpretedRegExpMacroAssembler::Expand");
     length_ = newLength;
 }
--- a/js/src/jit-test/tests/SIMD/swizzle.js
+++ b/js/src/jit-test/tests/SIMD/swizzle.js
@@ -79,8 +79,24 @@ testBailouts('one');
 testBailouts(true);
 
 try {
     testInt32x4SwizzleBailout();
     throw 'not caught';
 } catch(e) {
     assertEq(e instanceof TypeError, true);
 }
+
+(function() {
+    var zappa = 0;
+
+    function testBailouts() {
+        var i4 = SIMD.Int32x4(1, 2, 3, 4);
+        for (var i = 0; i < 300; i++) {
+            var value = i == 299 ? 2.5 : 1;
+            SIMD.Int32x4.swizzle(i4, value, 3, 2, 0);
+            zappa = i;
+        }
+    }
+
+    try { testBailouts(); } catch (e) {}
+    assertEq(zappa, 298);
+})();
--- a/js/src/jit-test/tests/baseline/bug945223.js
+++ b/js/src/jit-test/tests/baseline/bug945223.js
@@ -12,16 +12,18 @@
 // __noSuchMethod__ (Bug 964574) at the moment, which cause a differential
 // behaviour.
 //
 // As we hope to remote __noSuchMethod__ soon (Bug 683218), we disable the mode
 // which force the selection of IonMonkey ICs.
 if (getJitCompilerOptions()["ion.forceinlineCaches"])
     setJitCompilerOption("ion.forceinlineCaches", 0);
 
+enableNoSuchMethod();
+
 Array.prototype.__proto__ = Proxy.create({
     getPropertyDescriptor: function(name) {
 	return (560566);
     },
 }, null);
 function f() {}
 function g() {}
 var x = [f,f,f,undefined,g];
--- a/js/src/jit-test/tests/basic/bug717208.js
+++ b/js/src/jit-test/tests/basic/bug717208.js
@@ -1,8 +1,10 @@
+enableNoSuchMethod();
+
 var count = 0;
 var a = {__noSuchMethod__: function() { count++; } }
 
 function f() {
   for (var i = 0; i < 10; i++) {
     try {
       a.b();
     } catch (e) {
--- a/js/src/jit-test/tests/basic/bug732087.js
+++ b/js/src/jit-test/tests/basic/bug732087.js
@@ -1,6 +1,7 @@
+enableNoSuchMethod();
 gczeal(2,1);
 var count = 0;
 var a = {__noSuchMethod__: function() { count++; } }
 for (var i = 0; i < 10; i++) {
   a.b();
 }
--- a/js/src/jit-test/tests/gc/bug-1165966.js
+++ b/js/src/jit-test/tests/gc/bug-1165966.js
@@ -1,6 +1,6 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom; --no-ion
+// |jit-test| --no-threads; --no-ion
 load(libdir + 'oomTest.js');
 var g = newGlobal();
 oomTest(function() {
     Debugger(g);
 });
--- a/js/src/jit-test/tests/gc/bug-1171909.js
+++ b/js/src/jit-test/tests/gc/bug-1171909.js
@@ -1,3 +1,3 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom
+// |jit-test| --no-threads; allow-unhandlable-oom
 load(libdir + 'oomTest.js');
 oomTest((function(x) { assertEq(x + y + ex, 25); }));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1188290.js
@@ -0,0 +1,12 @@
+this.__proto__ = [];
+var T = TypedObject;
+var ObjectStruct = new T.StructType({f: T.Object});
+var o = new ObjectStruct();
+minorgc();
+function writeObject(o, v) {
+    o.f = v;
+    assertEq(typeof o.f, "object");
+}
+for (var i = 0; i < 5; i++)
+    writeObject(o, { toString: function() { return "helo"} }
+);
--- a/js/src/jit-test/tests/gc/oomInDebugger.js
+++ b/js/src/jit-test/tests/gc/oomInDebugger.js
@@ -1,5 +1,5 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom
+// |jit-test| --no-threads
 
 load(libdir + 'oomTest.js');
 var g = newGlobal();
 oomTest(() => Debugger(g));
--- a/js/src/jit-test/tests/gc/oomInFormatStackDump.js
+++ b/js/src/jit-test/tests/gc/oomInFormatStackDump.js
@@ -1,4 +1,4 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom; --no-threads
+// |jit-test| --no-threads
 
 load(libdir + 'oomTest.js');
 oomTest(() => getBacktrace({args: true, locals: true, thisprops: true}));
--- a/js/src/jit-test/tests/gc/oomInNewGlobal.js
+++ b/js/src/jit-test/tests/gc/oomInNewGlobal.js
@@ -1,4 +1,4 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom
+// |jit-test| --no-threads
 
 load(libdir + 'oomTest.js');
 oomTest(newGlobal);
--- a/js/src/jit-test/tests/gc/oomInParseFunction.js
+++ b/js/src/jit-test/tests/gc/oomInParseFunction.js
@@ -1,4 +1,4 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom
+// |jit-test| --no-threads
 
 load(libdir + 'oomTest.js');
 oomTest(() => eval("function f() {}"));
--- a/js/src/jit-test/tests/gc/oomInWeakMap.js
+++ b/js/src/jit-test/tests/gc/oomInWeakMap.js
@@ -1,7 +1,7 @@
-// |jit-test| --no-ggc; allow-unhandlable-oom; --no-threads
+// |jit-test| --no-threads
 
 load(libdir + 'oomTest.js');
 oomTest(function () {
     eval(`var wm = new WeakMap();
          wm.set({}, 'FOO').get(false);`);
 });
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1205842.js
@@ -0,0 +1,8 @@
+function f(x) {
+    (function() {
+        x = 0;
+    })();
+}
+for (var k = 0; k < 9; k++) {
+    f(Math.fround(1));
+}
--- a/js/src/jit-test/tests/ion/callelem.js
+++ b/js/src/jit-test/tests/ion/callelem.js
@@ -1,8 +1,10 @@
+enableNoSuchMethod();
+
 var res = 0;
 var o = {};
 o.__noSuchMethod__ = function(x) { res += x; };
 
 function f() {
     for (var i=0; i<80; i++) {
         o[i](i);
     }
--- a/js/src/jit-test/tests/jaeger/bug555922.js
+++ b/js/src/jit-test/tests/jaeger/bug555922.js
@@ -1,8 +1,10 @@
+enableNoSuchMethod();
+
 (function() {
   let(z) {
     for each(b in [{}]) { ({
         get __noSuchMethod__() { return Function }
       }).w()
     }
   }
 })()
--- a/js/src/jit-test/tests/jaeger/bug732423.js
+++ b/js/src/jit-test/tests/jaeger/bug732423.js
@@ -1,8 +1,10 @@
+enableNoSuchMethod();
+
 function testStuff(x, y) {
     for (var i = 0; i < 60; i++) {
         x[y]();
         x[y];
     }
 }
 testStuff({"elements":function(){}}, "elements");
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -317,18 +317,21 @@ MaybeFoldConditionBlock(MIRGraph& graph,
         if (testBlock->isLoopBackedge())
             return;
         testBlock = testBlock->getSuccessor(0);
         if (testBlock->numPredecessors() != 1)
             return;
     }
 
     // Make sure the test block does not have any outgoing loop backedges.
-    if (!SplitCriticalEdgesForBlock(graph, testBlock))
-        CrashAtUnhandlableOOM("MaybeFoldConditionBlock");
+    {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        if (!SplitCriticalEdgesForBlock(graph, testBlock))
+            oomUnsafe.crash("MaybeFoldConditionBlock");
+    }
 
     MPhi* phi;
     MTest* finalTest;
     if (!BlockIsSingleTest(phiBlock, testBlock, &phi, &finalTest))
         return;
 
     MDefinition* trueResult = phi->getOperand(phiBlock->indexForPredecessor(trueBranch));
     MDefinition* falseResult = phi->getOperand(phiBlock->indexForPredecessor(falseBranch));
@@ -2927,18 +2930,19 @@ LinearSum::add(MDefinition* term, int32_
             if (terms_[i].scale == 0) {
                 terms_[i] = terms_.back();
                 terms_.popBack();
             }
             return true;
         }
     }
 
+    AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!terms_.append(LinearTerm(term, scale)))
-        CrashAtUnhandlableOOM("LinearSum::add");
+        oomUnsafe.crash("LinearSum::add");
 
     return true;
 }
 
 bool
 LinearSum::add(int32_t constant)
 {
     return SafeAdd(constant, constant_, &constant_);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -11099,18 +11099,19 @@ IonBuilder::convertUnboxedObjects(MDefin
 
     BaselineInspector::ObjectGroupVector list(alloc());
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i);
         if (!key || !key->isGroup())
             continue;
 
         if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) {
+            AutoEnterOOMUnsafeRegion oomUnsafe;
             if (layout->nativeGroup() && !list.append(key->group()))
-                CrashAtUnhandlableOOM("IonBuilder::convertUnboxedObjects");
+                oomUnsafe.crash("IonBuilder::convertUnboxedObjects");
         }
     }
 
     return convertUnboxedObjects(obj, list);
 }
 
 MDefinition*
 IonBuilder::convertUnboxedObjects(MDefinition* obj,
--- a/js/src/jit/LoopUnroller.cpp
+++ b/js/src/jit/LoopUnroller.cpp
@@ -41,17 +41,17 @@ struct LoopUnroller
     MBasicBlock* oldPreheader;
     MBasicBlock* newPreheader;
 
     // Map terms in the original loop to terms in the current unrolled iteration.
     DefinitionMap unrolledDefinitions;
 
     MDefinition* getReplacementDefinition(MDefinition* def);
     MResumePoint* makeReplacementResumePoint(MBasicBlock* block, MResumePoint* rp);
-    void makeReplacementInstruction(MInstruction* ins);
+    bool makeReplacementInstruction(MInstruction* ins);
 
     void go(LoopIterationBound* bound);
 };
 
 } // namespace
 
 MDefinition*
 LoopUnroller::getReplacementDefinition(MDefinition* def)
@@ -71,54 +71,56 @@ LoopUnroller::getReplacementDefinition(M
         MConstant* constant = MConstant::New(alloc, def->toConstant()->value());
         oldPreheader->insertBefore(*oldPreheader->begin(), constant);
         return constant;
     }
 
     return p->value();
 }
 
-void
+bool
 LoopUnroller::makeReplacementInstruction(MInstruction* ins)
 {
     MDefinitionVector inputs(alloc);
     for (size_t i = 0; i < ins->numOperands(); i++) {
         MDefinition* old = ins->getOperand(i);
         MDefinition* replacement = getReplacementDefinition(old);
         if (!inputs.append(replacement))
-            CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
+            return false;
     }
 
     MInstruction* clone = ins->clone(alloc, inputs);
 
     unrolledBackedge->add(clone);
 
     if (!unrolledDefinitions.putNew(ins, clone))
-        CrashAtUnhandlableOOM("LoopUnroller::makeReplacementDefinition");
+        return false;
 
     if (MResumePoint* old = ins->resumePoint()) {
         MResumePoint* rp = makeReplacementResumePoint(unrolledBackedge, old);
         clone->setResumePoint(rp);
     }
+
+    return true;
 }
 
 MResumePoint*
 LoopUnroller::makeReplacementResumePoint(MBasicBlock* block, MResumePoint* rp)
 {
     MDefinitionVector inputs(alloc);
     for (size_t i = 0; i < rp->numOperands(); i++) {
         MDefinition* old = rp->getOperand(i);
         MDefinition* replacement = old->isUnused() ? old : getReplacementDefinition(old);
         if (!inputs.append(replacement))
-            CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
+            return nullptr;
     }
 
     MResumePoint* clone = MResumePoint::New(alloc, block, rp, inputs);
     if (!clone)
-        CrashAtUnhandlableOOM("LoopUnroller::makeReplacementResumePoint");
+        return nullptr;
 
     return clone;
 }
 
 void
 LoopUnroller::go(LoopIterationBound* bound)
 {
     // For now we always unroll loops the same number of times.
@@ -224,51 +226,57 @@ LoopUnroller::go(LoopIterationBound* bou
     newPreheader->discardAllResumePoints();
 
     // Insert new blocks at their RPO position, and update block ids.
     graph.insertBlockAfter(oldPreheader, unrolledHeader);
     graph.insertBlockAfter(unrolledHeader, unrolledBackedge);
     graph.insertBlockAfter(unrolledBackedge, newPreheader);
     graph.renumberBlocksAfter(oldPreheader);
 
+    // We don't tolerate allocation failure after this point.
+    // TODO: This is a bit drastic, is it possible to improve this?
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+
     if (!unrolledDefinitions.init())
-        CrashAtUnhandlableOOM("LoopUnroller::go");
+        oomUnsafe.crash("LoopUnroller::go");
 
     // Add phis to the unrolled loop header which correspond to the phis in the
     // original loop header.
     MOZ_ASSERT(header->getPredecessor(0) == oldPreheader);
     for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
         MPhi* old = *iter;
         MOZ_ASSERT(old->numOperands() == 2);
         MPhi* phi = MPhi::New(alloc);
         phi->setResultType(old->type());
         phi->setResultTypeSet(old->resultTypeSet());
         phi->setRange(old->range());
 
         unrolledHeader->addPhi(phi);
 
         if (!phi->reserveLength(2))
-            CrashAtUnhandlableOOM("LoopUnroller::go");
+            oomUnsafe.crash("LoopUnroller::go");
 
         // Set the first input for the phi for now. We'll set the second after
         // finishing the unroll.
         phi->addInput(old->getOperand(0));
 
         // The old phi will now take the value produced by the unrolled loop.
         old->replaceOperand(0, phi);
 
         if (!unrolledDefinitions.putNew(old, phi))
-            CrashAtUnhandlableOOM("LoopUnroller::go");
+            oomUnsafe.crash("LoopUnroller::go");
     }
 
     // The loop condition can bail out on e.g. integer overflow, so make a
     // resume point based on the initial resume point of the original header.
     MResumePoint* headerResumePoint = header->entryResumePoint();
     if (headerResumePoint) {
         MResumePoint* rp = makeReplacementResumePoint(unrolledHeader, headerResumePoint);
+        if (!rp)
+            oomUnsafe.crash("LoopUnroller::makeReplacementResumePoint");
         unrolledHeader->setEntryResumePoint(rp);
 
         // Perform an interrupt check at the start of the unrolled loop.
         unrolledHeader->add(MInterruptCheck::New(alloc));
     }
 
     // Generate code for the test in the unrolled loop.
     for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
@@ -280,53 +288,58 @@ LoopUnroller::go(LoopIterationBound* bou
     MTest* unrolledTest = MTest::New(alloc, compare, unrolledBackedge, newPreheader);
     unrolledHeader->end(unrolledTest);
 
     // Make an entry resume point for the unrolled body. The unrolled header
     // does not have side effects on stack values, even if the original loop
     // header does, so use the same resume point as for the unrolled header.
     if (headerResumePoint) {
         MResumePoint* rp = makeReplacementResumePoint(unrolledBackedge, headerResumePoint);
+        if (!rp)
+            oomUnsafe.crash("LoopUnroller::makeReplacementResumePoint");
         unrolledBackedge->setEntryResumePoint(rp);
     }
 
     // Make an entry resume point for the new preheader. There are no
     // instructions which use this but some other stuff wants one to be here.
     if (headerResumePoint) {
         MResumePoint* rp = makeReplacementResumePoint(newPreheader, headerResumePoint);
+        if (!rp)
+            oomUnsafe.crash("LoopUnroller::makeReplacementResumePoint");
         newPreheader->setEntryResumePoint(rp);
     }
 
     // Generate the unrolled code.
     MOZ_ASSERT(UnrollCount > 1);
     size_t unrollIndex = 0;
     while (true) {
         // Clone the contents of the original loop into the unrolled loop body.
         for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
             MBasicBlock* block = bodyBlocks[i];
             for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
                 MInstruction* ins = *iter;
                 if (ins->canClone()) {
-                    makeReplacementInstruction(*iter);
+                    if (!makeReplacementInstruction(*iter))
+                        oomUnsafe.crash("LoopUnroller::makeReplacementDefinition");
                 } else {
                     // Control instructions are handled separately.
                     MOZ_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck());
                 }
             }
         }
 
         // Compute the value of each loop header phi after the execution of
         // this unrolled iteration.
         MDefinitionVector phiValues(alloc);
         MOZ_ASSERT(header->getPredecessor(1) == backedge);
         for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
             MPhi* old = *iter;
             MDefinition* oldInput = old->getOperand(1);
             if (!phiValues.append(getReplacementDefinition(oldInput)))
-                CrashAtUnhandlableOOM("LoopUnroller::go");
+                oomUnsafe.crash("LoopUnroller::go");
         }
 
         unrolledDefinitions.clear();
 
         if (unrollIndex == UnrollCount - 1) {
             // We're at the end of the last unrolled iteration, set the
             // backedge input for the unrolled loop phis.
             size_t phiIndex = 0;
@@ -338,17 +351,17 @@ LoopUnroller::go(LoopIterationBound* bou
             break;
         }
 
         // Update the map for the phis in the next iteration.
         size_t phiIndex = 0;
         for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) {
             MPhi* old = *iter;
             if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++]))
-                CrashAtUnhandlableOOM("LoopUnroller::go");
+                oomUnsafe.crash("LoopUnroller::go");
         }
         MOZ_ASSERT(phiIndex == phiValues.length());
 
         unrollIndex++;
     }
 
     MGoto* backedgeJump = MGoto::New(alloc, unrolledHeader);
     unrolledBackedge->end(backedgeJump);
@@ -358,17 +371,17 @@ LoopUnroller::go(LoopIterationBound* bou
     oldPreheader->discardLastIns();
     oldPreheader->end(MGoto::New(alloc, unrolledHeader));
 
     // Place the new preheader before the original loop.
     newPreheader->end(MGoto::New(alloc, header));
 
     // Cleanup the MIR graph.
     if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge))
-        CrashAtUnhandlableOOM("LoopUnroller::go");
+        oomUnsafe.crash("LoopUnroller::go");
     header->replacePredecessor(oldPreheader, newPreheader);
     oldPreheader->setSuccessorWithPhis(unrolledHeader, 0);
     newPreheader->setSuccessorWithPhis(header, 0);
     unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1);
 }
 
 bool
 jit::UnrollLoops(MIRGraph& graph, const LoopIterationBoundVector& bounds)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1848,16 +1848,17 @@ class MSimdGeneralShuffle :
     MSimdGeneralShuffle(unsigned numVectors, unsigned numLanes, MIRType type)
       : numVectors_(numVectors), numLanes_(numLanes)
     {
         MOZ_ASSERT(IsSimdType(type));
         MOZ_ASSERT(SimdTypeToLength(type) == numLanes_);
 
         setResultType(type);
         specialization_ = type;
+        setGuard(); // throws if lane index is out of bounds
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(SimdGeneralShuffle);
 
     static MSimdGeneralShuffle* New(TempAllocator& alloc, unsigned numVectors, unsigned numLanes,
                                     MIRType type)
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -102,18 +102,19 @@ MIRGenerator::abort(const char* message,
 
 void
 MIRGenerator::addAbortedPreliminaryGroup(ObjectGroup* group)
 {
     for (size_t i = 0; i < abortedPreliminaryGroups_.length(); i++) {
         if (group == abortedPreliminaryGroups_[i])
             return;
     }
+    AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!abortedPreliminaryGroups_.append(group))
-        CrashAtUnhandlableOOM("addAbortedPreliminaryGroup");
+        oomUnsafe.crash("addAbortedPreliminaryGroup");
 }
 
 bool
 MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
 {
     // A heap access needs a bounds-check branch if we're not relying on signal
     // handlers to catch errors, and if it's not proven to be within bounds.
     // We use signal-handlers on x64, but on x86 there isn't enough address
@@ -1132,26 +1133,28 @@ MBasicBlock::addPredecessorSameInputsAs(
 {
     MOZ_ASSERT(pred);
     MOZ_ASSERT(predecessors_.length() > 0);
 
     // Predecessors must be finished, and at the correct stack depth.
     MOZ_ASSERT(pred->hasLastIns());
     MOZ_ASSERT(!pred->successorWithPhis());
 
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+
     if (!phisEmpty()) {
         size_t existingPosition = indexForPredecessor(existingPred);
         for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
             if (!iter->addInputSlow(iter->getOperand(existingPosition)))
-                CrashAtUnhandlableOOM("MBasicBlock::addPredecessorAdjustPhis");
+                oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
         }
     }
 
     if (!predecessors_.append(pred))
-        CrashAtUnhandlableOOM("MBasicBlock::addPredecessorAdjustPhis");
+        oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
 }
 
 bool
 MBasicBlock::addPredecessorWithoutPhis(MBasicBlock* pred)
 {
     // Predecessors must be finished.
     MOZ_ASSERT(pred && pred->hasLastIns());
     return predecessors_.append(pred);
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2286,16 +2286,20 @@ RangeAnalysis::addRangeAssertions()
                 continue;
 
             Range r(ins);
 
             // Don't insert assertions if there's nothing interesting to assert.
             if (r.isUnknown() || (ins->type() == MIRType_Int32 && r.isUnknownInt32()))
                 continue;
 
+            // Don't add a use to an instruction that is recovered on bailout.
+            if (ins->isRecoveredOnBailout())
+                continue;
+
             MAssertRange* guard = MAssertRange::New(alloc(), ins, new(alloc()) Range(r));
 
             // Beta nodes and interrupt checks are required to be located at the
             // beginnings of basic blocks, so we must insert range assertions
             // after any such instructions.
             MInstruction* insertAt = block->safeInsertTop(ins);
 
             if (insertAt == *iter)
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -962,19 +962,20 @@ static CachePage*
 GetCachePageLocked(Simulator::ICacheMap& i_cache, void* page)
 {
     MOZ_ASSERT(Simulator::ICacheCheckingEnabled);
 
     Simulator::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
     if (p)
         return p->value();
 
+    AutoEnterOOMUnsafeRegion oomUnsafe;
     CachePage* new_page = js_new<CachePage>();
     if (!new_page || !i_cache.add(p, page, new_page))
-        CrashAtUnhandlableOOM("Simulator CachePage");
+        oomUnsafe.crash("Simulator CachePage");
 
     return new_page;
 }
 
 // Flush from start up to and not including start + size.
 static void
 FlushOnePageLocked(Simulator::ICacheMap& i_cache, intptr_t start, int size)
 {
@@ -1192,19 +1193,20 @@ class Redirection
         Redirection* current = sim->redirection();
         for (; current != nullptr; current = current->next_) {
             if (current->nativeFunction_ == nativeFunction) {
                 MOZ_ASSERT(current->type() == type);
                 return current;
             }
         }
 
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
         if (!redir)
-            CrashAtUnhandlableOOM("Simulator redirection");
+            oomUnsafe.crash("Simulator redirection");
         new(redir) Redirection(nativeFunction, type, sim);
         return redir;
     }
 
     static Redirection* FromSwiInstruction(SimInstruction* swiInstruction) {
         uint8_t* addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction);
         uint8_t* addrOfRedirection = addrOfSwi - offsetof(Redirection, swiInstruction_);
         return reinterpret_cast<Redirection*>(addrOfRedirection);
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -406,19 +406,8 @@ LIRGeneratorMIPSShared::visitAsmJSAtomic
     MOZ_CRASH("NYI");
 }
 
 void
 LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
 {
     MOZ_CRASH("NYI");
 }
-
-void
-LIRGeneratorMIPSShared::visitRandom(MRandom* ins)
-{
-    LRandom *lir = new(alloc()) LRandom(temp(),
-                                        temp(),
-                                        temp(),
-                                        temp(),
-                                        temp());
-    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
-}
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -84,15 +84,14 @@ class LIRGeneratorMIPSShared : public LI
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
-    void visitRandom(MRandom* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_Lowering_mips_shared_h */
--- a/js/src/jit/mips32/Lowering-mips32.h
+++ b/js/src/jit/mips32/Lowering-mips32.h
@@ -34,16 +34,17 @@ class LIRGeneratorMIPS : public LIRGener
 
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
     void lowerTruncateFToInt32(MTruncateToInt32* ins);
 
   public:
     void visitBox(MBox* box);
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips32_Lowering_mips32_h */
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -1916,16 +1916,41 @@ MacroAssemblerMIPSCompat::storePtr(Regis
 
 void
 MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
 {
     movePtr(ImmPtr(dest.addr), ScratchRegister);
     storePtr(src, Address(ScratchRegister, 0));
 }
 
+void
+MacroAssemblerMIPSCompat::clampIntToUint8(Register reg)
+{
+    // look at (reg >> 8) if it is 0, then src shouldn't be clamped
+    // if it is <0, then we want to clamp to 0,
+    // otherwise, we wish to clamp to 255
+    Label done;
+    ma_move(ScratchRegister, reg);
+    asMasm().rshiftPtrArithmetic(Imm32(8), ScratchRegister);
+    ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
+    {
+        Label negative;
+        ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
+        {
+            ma_li(reg, Imm32(255));
+            ma_b(&done, ShortJump);
+        }
+        bind(&negative);
+        {
+            ma_move(reg, zero);
+        }
+    }
+    bind(&done);
+}
+
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     MOZ_ASSERT(input != ScratchDoubleReg);
     Label positive, done;
 
     // <= 0 or NaN --> 0
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -1282,38 +1282,17 @@ class MacroAssemblerMIPSCompat : public 
         ma_ss(src, addr);
     }
 
     void zeroDouble(FloatRegister reg) {
         moveToDoubleLo(zero, reg);
         moveToDoubleHi(zero, reg);
     }
 
-    void clampIntToUint8(Register reg) {
-        // look at (reg >> 8) if it is 0, then src shouldn't be clamped
-        // if it is <0, then we want to clamp to 0,
-        // otherwise, we wish to clamp to 255
-        Label done;
-        ma_move(ScratchRegister, reg);
-        as_sra(ScratchRegister, ScratchRegister, 8);
-        ma_b(ScratchRegister, ScratchRegister, &done, Assembler::Zero, ShortJump);
-        {
-            Label negative;
-            ma_b(ScratchRegister, ScratchRegister, &negative, Assembler::Signed, ShortJump);
-            {
-                ma_li(reg, Imm32(255));
-                ma_b(&done, ShortJump);
-            }
-            bind(&negative);
-            {
-                ma_move(reg, zero);
-            }
-        }
-        bind(&done);
-    }
+    void clampIntToUint8(Register reg);
 
     void subPtr(Imm32 imm, const Register dest);
     void subPtr(const Address& addr, const Register dest);
     void subPtr(Register src, const Address& dest);
     void addPtr(Imm32 imm, const Register dest);
     void addPtr(Imm32 imm, const Address& dest);
     void addPtr(ImmWord imm, const Register dest) {
         addPtr(Imm32(imm.value), dest);
@@ -1369,18 +1348,18 @@ class MacroAssemblerMIPSCompat : public 
     bool buildOOLFakeExitFrame(void* fakeReturnAddr);
 
   public:
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(nextOffset().getOffset());
     }
 
     void memIntToValue(Address Source, Address Dest) {
-        load32(Source, SecondScratchReg);
-        storeValue(JSVAL_TYPE_INT32, SecondScratchReg, Dest);
+        load32(Source, ScratchRegister);
+        storeValue(JSVAL_TYPE_INT32, ScratchRegister, Dest);
     }
 
     void lea(Operand addr, Register dest) {
         ma_addu(dest, addr.baseReg(), Imm32(addr.disp()));
     }
 
     void abiret() {
         as_jr(ra);
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -13,16 +13,17 @@ UNIFIED_SOURCES += [
     'testArrayBuffer.cpp',
     'testArrayBufferView.cpp',
     'testBug604087.cpp',
     'testCallNonGenericMethodOnProxy.cpp',
     'testChromeBuffer.cpp',
     'testClassGetter.cpp',
     'testCloneScript.cpp',
     'testContexts.cpp',
+    'testDateToLocaleString.cpp',
     'testDebugger.cpp',
     'testDeepFreeze.cpp',
     'testDefineGetterSetterNonEnumerable.cpp',
     'testDefineProperty.cpp',
     'testDefinePropertyIgnoredAttributes.cpp',
     'testDifferentNewTargetInvokeConstructor.cpp',
     'testEnclosingFunction.cpp',
     'testErrorCopying.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testDateToLocaleString.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* 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 "jsapi-tests/tests.h"
+
+BEGIN_TEST(testDateToLocaleString)
+{
+    // This test should only attempt to run if we have Intl support: necessary
+    // to properly assume that changes to the default locale will predictably
+    // affect the behavior of the locale-sensitive Date methods tested here.
+    JS::Rooted<JS::Value> haveIntl(cx);
+    EVAL("typeof Intl !== 'undefined'", &haveIntl);
+    if (!haveIntl.toBoolean())
+        return true;
+
+    // Pervasive assumption: our Intl support includes "de" (German) and
+    // "en" (English) and treats them differently for purposes of
+    // Date.prototype.toLocale{,Date,Time}String behavior.
+
+    // Start with German.
+    CHECK(JS_SetDefaultLocale(rt, "de"));
+
+    // The (constrained) Date object we'll use to test behavior.
+    EXEC("var d = new Date(Date.UTC(2015, 9 - 1, 17));");
+
+    // Test that toLocaleString behavior changes with default locale changes.
+    EXEC("var deAll = d.toLocaleString();");
+
+    CHECK(JS_SetDefaultLocale(rt, "en"));
+    EXEC("if (d.toLocaleString() === deAll) \n"
+         "  throw 'toLocaleString results should have changed with system locale change';");
+
+    // Test that toLocaleDateString behavior changes with default locale changes.
+    EXEC("var enDate = d.toLocaleDateString();");
+
+    CHECK(JS_SetDefaultLocale(rt, "de"));
+    EXEC("if (d.toLocaleDateString() === enDate) \n"
+         "  throw 'toLocaleDateString results should have changed with system locale change';");
+
+    // Test that toLocaleTimeString behavior changes with default locale changes.
+    EXEC("var deTime = d.toLocaleTimeString();");
+
+    CHECK(JS_SetDefaultLocale(rt, "en"));
+    EXEC("if (d.toLocaleTimeString() === deTime) \n"
+         "  throw 'toLocaleTimeString results should have changed with system locale change';");
+
+    JS_ResetDefaultLocale(rt);
+    return true;
+}
+END_TEST(testDateToLocaleString)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4084,17 +4084,17 @@ JS::CompileForNonSyntacticScope(JSContex
                                 const char* filename, JS::MutableHandleScript script)
 {
     return ::Compile(cx, options, HasNonSyntacticScope, filename, script);
 }
 
 JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
 {
-    static const size_t TINY_LENGTH = 1000;
+    static const size_t TINY_LENGTH = 5 * 1000;
     static const size_t HUGE_LENGTH = 100 * 1000;
 
     // These are heuristics which the caller may choose to ignore (e.g., for
     // testing purposes).
     if (!options.forceAsync) {
         // Compiling off the main thread inolves creating a new Zone and other
         // significant overheads.  Don't bother if the script is tiny.
         if (length < TINY_LENGTH)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1151,17 +1151,18 @@ class JS_PUBLIC_API(RuntimeOptions) {
       : baseline_(true),
         ion_(true),
         asmJS_(true),
         nativeRegExp_(true),
         unboxedArrays_(false),
         asyncStack_(true),
         werror_(false),
         strictMode_(false),
-        extraWarnings_(false)
+        extraWarnings_(false),
+        noSuchMethod_(false)
     {
     }
 
     bool baseline() const { return baseline_; }
     RuntimeOptions& setBaseline(bool flag) {
         baseline_ = flag;
         return *this;
     }
@@ -1233,26 +1234,33 @@ class JS_PUBLIC_API(RuntimeOptions) {
         extraWarnings_ = flag;
         return *this;
     }
     RuntimeOptions& toggleExtraWarnings() {
         extraWarnings_ = !extraWarnings_;
         return *this;
     }
 
+    bool noSuchMethod() const { return noSuchMethod_; }
+    RuntimeOptions& setNoSuchMethod(bool flag) {
+        noSuchMethod_ = flag;
+        return *this;
+    }
+
   private:
     bool baseline_ : 1;
     bool ion_ : 1;
     bool asmJS_ : 1;
     bool nativeRegExp_ : 1;
     bool unboxedArrays_ : 1;
     bool asyncStack_ : 1;
     bool werror_ : 1;
     bool strictMode_ : 1;
     bool extraWarnings_ : 1;
+    bool noSuchMethod_ : 1;
 };
 
 JS_PUBLIC_API(RuntimeOptions&)
 RuntimeOptionsRef(JSRuntime* rt);
 
 JS_PUBLIC_API(RuntimeOptions&)
 RuntimeOptionsRef(JSContext* cx);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -52,19 +52,51 @@ using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::CeilingLog2;
 using mozilla::CheckedInt;
 using mozilla::DebugOnly;
 using mozilla::IsNaN;
 using mozilla::UniquePtr;
 
 using JS::AutoCheckCannotGC;
+using JS::IsArrayAnswer;
 using JS::ToUint32;
 
 bool
+JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer)
+{
+    if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>()) {
+        *answer = IsArrayAnswer::Array;
+        return true;
+    }
+
+    if (obj->is<ProxyObject>())
+        return Proxy::isArray(cx, obj, answer);
+
+    *answer = IsArrayAnswer::NotArray;
+    return true;
+}
+
+bool
+JS::IsArray(JSContext* cx, HandleObject obj, bool* isArray)
+{
+    IsArrayAnswer answer;
+    if (!IsArray(cx, obj, &answer))
+        return false;
+
+    if (answer == IsArrayAnswer::RevokedProxy) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
+        return false;
+    }
+
+    *isArray = answer == IsArrayAnswer::Array;
+    return true;
+}
+
+bool
 js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp)
 {
     if (obj->is<ArrayObject>()) {
         *lengthp = obj->as<ArrayObject>().length();
         return true;
     }
 
     if (obj->is<UnboxedArrayObject>()) {
@@ -1991,18 +2023,23 @@ js::array_push(JSContext* cx, unsigned a
                 return false;
 
             uint32_t newlength = length + args.length();
             args.rval().setNumber(newlength);
 
             // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the
             // length for boxed and unboxed arrays. Handle updates to the length of
             // non-arrays here.
-            if (!IsArray(obj, cx))
+            bool isArray;
+            if (!IsArray(cx, obj, &isArray))
+                return false;
+
+            if (!isArray)
                 return SetLengthProperty(cx, obj, newlength);
+
             return true;
         }
     }
 
     /* Steps 4-5. */
     if (!InitArrayElements(cx, obj, length, args.length(), args.array()))
         return false;
 
@@ -2585,17 +2622,20 @@ js::array_concat(JSContext* cx, unsigned
 
     /* Create a new Array object and root it using *vp. */
     RootedObject aobj(cx, ToObject(cx, args.thisv()));
     if (!aobj)
         return false;
 
     RootedObject narr(cx);
     uint32_t length;
-    if (IsArray(aobj, cx) && !ObjectMayHaveExtraIndexedProperties(aobj)) {
+    bool isArray;
+    if (!IsArray(cx, aobj, &isArray))
+        return false;
+    if (isArray && !ObjectMayHaveExtraIndexedProperties(aobj)) {
         if (!GetLengthProperty(cx, aobj, &length))
             return false;
 
         size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(aobj);
         narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
         if (!narr)
             return false;
         CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
@@ -2610,17 +2650,20 @@ js::array_concat(JSContext* cx, unsigned
         if (length == initlen) {
             while (argc) {
                 HandleValue v = HandleValue::fromMarkedLocation(p);
                 if (!v.isObject())
                     break;
                 RootedObject obj(cx, &v.toObject());
 
                 // This should be IsConcatSpreadable
-                if (!IsArray(obj, cx) || ObjectMayHaveExtraIndexedProperties(obj))
+                bool isArray;
+                if (!IsArray(cx, obj, &isArray))
+                    return false;
+                if (!isArray || ObjectMayHaveExtraIndexedProperties(obj))
                     break;
 
                 uint32_t argLength;
                 if (!GetLengthProperty(cx, obj, &argLength))
                     return false;
 
                 initlen = GetAnyBoxedOrUnboxedInitializedLength(obj);
                 if (argLength != initlen)
@@ -2666,17 +2709,20 @@ js::array_concat(JSContext* cx, unsigned
     /* Loop over [0, argc] to concat args into narr, expanding all Arrays. */
     for (unsigned i = 0; i <= argc; i++) {
         if (!CheckForInterrupt(cx))
             return false;
         HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
         if (v.isObject()) {
             RootedObject obj(cx, &v.toObject());
             // This should be IsConcatSpreadable
-            if (IsArray(obj, cx)) {
+            bool isArray;
+            if (!IsArray(cx, obj, &isArray))
+                return false;
+            if (isArray) {
                 uint32_t alength;
                 if (!GetLengthProperty(cx, obj, &alength))
                     return false;
                 RootedValue tmp(cx);
                 for (uint32_t slot = 0; slot < alength; slot++) {
                     bool hole;
                     if (!CheckForInterrupt(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
                         return false;
@@ -2989,17 +3035,18 @@ js::array_slice_dense(JSContext* cx, Han
 
 static bool
 array_isArray(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     bool isArray = false;
     if (args.get(0).isObject()) {
         RootedObject obj(cx, &args[0].toObject());
-        isArray = IsArray(obj, cx);
+        if (!IsArray(cx, obj, &isArray))
+            return false;
     }
     args.rval().setBoolean(isArray);
     return true;
 }
 
 static bool
 IsArrayConstructor(const Value& v)
 {
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -62,17 +62,16 @@ JSCompartment::JSCompartment(Zone* zone,
     globalWriteBarriered(false),
     neuteredTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     propertyTree(thisForCtor()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     lazyArrayBuffers(nullptr),
     gcIncomingGrayPointers(nullptr),
-    gcWeakMapList(nullptr),
     gcPreserveJitCode(options.preserveJitCode()),
     debugModeBits(0),
     rngState(0),
     watchpointMap(nullptr),
     scriptCountsMap(nullptr),
     debugScriptMap(nullptr),
     debugScopes(nullptr),
     enumerators(nullptr),
@@ -654,23 +653,16 @@ void
 JSCompartment::sweepDebugScopes()
 {
     JSRuntime* rt = runtimeFromAnyThread();
     if (debugScopes)
         debugScopes->sweep(rt);
 }
 
 void
-JSCompartment::sweepWeakMaps()
-{
-    /* Finalize unreachable (key,value) pairs in all weak maps. */
-    WeakMapBase::sweepCompartment(this);
-}
-
-void
 JSCompartment::sweepNativeIterators()
 {
     /* Sweep list of native iterators. */
     NativeIterator* ni = enumerators->next();
     while (ni != enumerators) {
         JSObject* iterObj = ni->iterObj();
         NativeIterator* next = ni->next();
         if (gc::IsAboutToBeFinalizedUnbarriered(&iterObj))
@@ -778,17 +770,16 @@ JSCompartment::clearTables()
     global_.set(nullptr);
 
     // No scripts should have run in this compartment. This is used when
     // merging a compartment that has been used off thread into another
     // compartment and zone.
     MOZ_ASSERT(crossCompartmentWrappers.empty());
     MOZ_ASSERT(!jitCompartment_);
     MOZ_ASSERT(!debugScopes);
-    MOZ_ASSERT(!gcWeakMapList);
     MOZ_ASSERT(enumerators->next() == enumerators);
     MOZ_ASSERT(regExps.empty());
 
     objectGroups.clearTables();
     if (baseShapes.initialized())
         baseShapes.clear();
     if (initialShapes.initialized())
         initialShapes.clear();
@@ -814,24 +805,25 @@ JSCompartment::clearObjectMetadata()
 }
 
 void
 JSCompartment::setNewObjectMetadata(JSContext* cx, JSObject* obj)
 {
     assertSameCompartment(cx, this, obj);
 
     if (JSObject* metadata = objectMetadataCallback(cx, obj)) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         assertSameCompartment(cx, metadata);
         if (!objectMetadataTable) {
             objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
             if (!objectMetadataTable || !objectMetadataTable->init())
-                CrashAtUnhandlableOOM("setNewObjectMetadata");
+                oomUnsafe.crash("setNewObjectMetadata");
         }
         if (!objectMetadataTable->add(cx, obj, metadata))
-            CrashAtUnhandlableOOM("setNewObjectMetadata");
+            oomUnsafe.crash("setNewObjectMetadata");
     }
 }
 
 static bool
 AddInnerLazyFunctionsFromScript(JSScript* script, AutoObjectVector& lazyFunctions)
 {
     if (!script->hasObjects())
         return true;
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -449,19 +449,16 @@ struct JSCompartment
      * During GC, stores the head of a list of incoming pointers from gray cells.
      *
      * The objects in the list are either cross-compartment wrappers, or
      * debugger wrapper objects.  The list link is either in the second extra
      * slot for the former, or a special slot for the latter.
      */
     JSObject*                    gcIncomingGrayPointers;
 
-    /* Linked list of live weakmaps in this compartment. */
-    js::WeakMapBase*             gcWeakMapList;
-
   private:
     /* Whether to preserve JIT code on non-shrinking GCs. */
     bool                         gcPreserveJitCode;
 
     enum {
         IsDebuggee = 1 << 0,
         DebuggerObservesAllExecution = 1 << 1,
         DebuggerObservesAsmJS = 1 << 2,
@@ -541,17 +538,16 @@ struct JSCompartment
     void sweepCrossCompartmentWrappers();
     void sweepSavedStacks();
     void sweepGlobalObject(js::FreeOp* fop);
     void sweepObjectPendingMetadata();
     void sweepSelfHostingScriptSource();
     void sweepJitCompartment(js::FreeOp* fop);
     void sweepRegExps();
     void sweepDebugScopes();
-    void sweepWeakMaps();
     void sweepNativeIterators();
     void sweepTemplateObjects();
 
     void purge();
     void clearTables();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupInitialShapeTable();
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -65,16 +65,19 @@ extern JS_FRIEND_API(JSObject*)
 JS_NewObjectWithoutMetadata(JSContext* cx, const JSClass* clasp, JS::Handle<JSObject*> proto);
 
 extern JS_FRIEND_API(uint32_t)
 JS_ObjectCountDynamicSlots(JS::HandleObject obj);
 
 extern JS_FRIEND_API(size_t)
 JS_SetProtoCalled(JSContext* cx);
 
+extern JS_FRIEND_API(bool)
+JS_ImmutablePrototypesEnabled();
+
 extern JS_FRIEND_API(size_t)
 JS_GetCustomIteratorCount(JSContext* cx);
 
 extern JS_FRIEND_API(bool)
 JS_NondeterministicGetWeakMapKeys(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject ret);
 
 // Raw JSScript* because this needs to be callable from a signal handler.
 extern JS_FRIEND_API(unsigned)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2010,29 +2010,40 @@ ArenaList::pickArenasToRelocate(size_t& 
 #ifdef DEBUG
 inline bool
 PtrIsInRange(const void* ptr, const void* start, size_t length)
 {
     return uintptr_t(ptr) - uintptr_t(start) < length;
 }
 #endif
 
-static bool
+static TenuredCell*
+AllocRelocatedCell(Zone* zone, AllocKind thingKind, size_t thingSize)
+{
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+    void* dstAlloc = zone->arenas.allocateFromFreeList(thingKind, thingSize);
+    if (!dstAlloc)
+        dstAlloc = GCRuntime::refillFreeListInGC(zone, thingKind);
+    if (!dstAlloc) {
+        // This can only happen in zeal mode or debug builds as we don't
+        // otherwise relocate more cells than we have existing free space
+        // for.
+        oomUnsafe.crash("Could not allocate new arena while compacting");
+    }
+    return TenuredCell::fromPointer(dstAlloc);
+}
+
+static void
 RelocateCell(Zone* zone, TenuredCell* src, AllocKind thingKind, size_t thingSize)
 {
     JS::AutoSuppressGCAnalysis nogc(zone->runtimeFromMainThread());
 
     // Allocate a new cell.
     MOZ_ASSERT(zone == src->zone());
-    void* dstAlloc = zone->arenas.allocateFromFreeList(thingKind, thingSize);
-    if (!dstAlloc)
-        dstAlloc = GCRuntime::refillFreeListInGC(zone, thingKind);
-    if (!dstAlloc)
-        return false;
-    TenuredCell* dst = TenuredCell::fromPointer(dstAlloc);
+    TenuredCell* dst = AllocRelocatedCell(zone, thingKind, thingSize);
 
     // Copy source cell contents to destination.
     memcpy(dst, src, thingSize);
 
     if (IsObjectAllocKind(thingKind)) {
         JSObject* srcObj = static_cast<JSObject*>(static_cast<Cell*>(src));
         JSObject* dstObj = static_cast<JSObject*>(static_cast<Cell*>(dst));
 
@@ -2063,40 +2074,33 @@ RelocateCell(Zone* zone, TenuredCell* sr
     }
 
     // Copy the mark bits.
     dst->copyMarkBitsFrom(src);
 
     // Mark source cell as forwarded and leave a pointer to the destination.
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
-
-    return true;
 }
 
 static void
 RelocateArena(ArenaHeader* aheader, SliceBudget& sliceBudget)
 {
     MOZ_ASSERT(aheader->allocated());
     MOZ_ASSERT(!aheader->hasDelayedMarking);
     MOZ_ASSERT(!aheader->markOverflow);
     MOZ_ASSERT(!aheader->allocatedDuringIncremental);
 
     Zone* zone = aheader->zone;
 
     AllocKind thingKind = aheader->getAllocKind();
     size_t thingSize = aheader->getThingSize();
 
     for (ArenaCellIterUnderFinalize i(aheader); !i.done(); i.next()) {
-        if (!RelocateCell(zone, i.getCell(), thingKind, thingSize)) {
-            // This can only happen in zeal mode or debug builds as we don't
-            // otherwise relocate more cells than we have existing free space
-            // for.
-            CrashAtUnhandlableOOM("Could not allocate new arena while compacting");
-        }
+        RelocateCell(zone, i.getCell(), thingKind, thingSize);
         sliceBudget.step();
     }
 
 #ifdef DEBUG
     for (ArenaCellIterUnderFinalize i(aheader); !i.done(); i.next()) {
         TenuredCell* src = i.getCell();
         MOZ_ASSERT(RelocationOverlay::isCellForwarded(src));
         TenuredCell* dest = Forwarded(src);
@@ -2294,30 +2298,30 @@ GCRuntime::sweepTypesAfterCompacting(Zon
 
 void
 GCRuntime::sweepZoneAfterCompacting(Zone* zone)
 {
     MOZ_ASSERT(zone->isCollecting());
     FreeOp* fop = rt->defaultFreeOp();
     sweepTypesAfterCompacting(zone);
     zone->sweepBreakpoints(fop);
+    zone->sweepWeakMaps();
 
     for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
         c->sweepInnerViews();
         c->sweepBaseShapeTable();
         c->sweepInitialShapeTable();
         c->objectGroups.sweep(fop);
         c->sweepRegExps();
         c->sweepSavedStacks();
         c->sweepGlobalObject(fop);
         c->sweepObjectPendingMetadata();
         c->sweepSelfHostingScriptSource();
         c->sweepDebugScopes();
         c->sweepJitCompartment(fop);
-        c->sweepWeakMaps();
         c->sweepNativeIterators();
         c->sweepTemplateObjects();
     }
 }
 
 template <typename T>
 static void
 UpdateCellPointersTyped(MovingTracer* trc, ArenaHeader* arena, JS::TraceKind traceKind)
@@ -2627,19 +2631,19 @@ GCRuntime::updatePointersToRelocatedCell
     // Mark roots to update them.
     {
         markRuntime(&trc, MarkRuntime);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
         Debugger::markIncomingCrossCompartmentEdges(&trc);
 
+        WeakMapBase::markAll(zone, &trc);
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
             c->trace(&trc);
-            WeakMapBase::markAll(c, &trc);
             if (c->watchpointMap)
                 c->watchpointMap->markAll(&trc);
         }
 
         // Mark all gray roots, making sure we call the trace callback to get the
         // current set.
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&trc, grayRootTracer.data);
@@ -3385,18 +3389,22 @@ GCHelperState::setState(State state)
 }
 
 void
 GCHelperState::startBackgroundThread(State newState)
 {
     MOZ_ASSERT(!thread && state() == IDLE && newState != IDLE);
     setState(newState);
 
-    if (!HelperThreadState().gcHelperWorklist().append(this))
-        CrashAtUnhandlableOOM("Could not add to pending GC helpers list");
+    {
+        AutoEnterOOMUnsafeRegion noOOM;
+        if (!HelperThreadState().gcHelperWorklist().append(this))
+            noOOM.crash("Could not add to pending GC helpers list");
+    }
+
     HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
 }
 
 void
 GCHelperState::waitForBackgroundThread()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
@@ -3934,19 +3942,19 @@ GCRuntime::beginMarkPhase(JS::gcreason::
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_UNMARK);
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             /* Unmark everything in the zones being collected. */
             zone->arenas.unmarkAll();
         }
 
-        for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
-            /* Unmark all weak maps in the compartments being collected. */
-            WeakMapBase::unmarkCompartment(c);
+        for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+            /* Unmark all weak maps in the zones being collected. */
+            WeakMapBase::unmarkZone(zone);
         }
 
         if (isFull)
             UnmarkScriptData(rt);
     }
 
     markRuntime(gcmarker, MarkRuntime);
 
@@ -4018,37 +4026,39 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         if (!c->maybeAlive && !rt->isAtomsCompartment(c))
             c->scheduledForDestruction = true;
     }
     foundBlackGrayEdges = false;
 
     return true;
 }
 
-template <class CompartmentIterT>
+template <class ZoneIterT>
 void
 GCRuntime::markWeakReferences(gcstats::Phase phase)
 {
     MOZ_ASSERT(marker.isDrained());
 
     gcstats::AutoPhase ap1(stats, phase);
 
     marker.enterWeakMarkingMode();
 
     // TODO bug 1167452: Make weak marking incremental
     SliceBudget budget = SliceBudget::unlimited();
     marker.drainMarkStack(budget);
 
     for (;;) {
         bool markedAny = false;
-        for (CompartmentIterT c(rt); !c.done(); c.next()) {
+        if (!marker.isWeakMarkingTracer()) {
+            for (ZoneIterT zone(rt); !zone.done(); zone.next())
+                markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
+        }
+        for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) {
             if (c->watchpointMap)
                 markedAny |= c->watchpointMap->markIteratively(&marker);
-            if (!marker.isWeakMarkingTracer())
-                markedAny |= WeakMapBase::markCompartmentIteratively(c, &marker);
         }
         markedAny |= Debugger::markAllIteratively(&marker);
         markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
 
         if (!markedAny)
             break;
 
         auto unlimited = SliceBudget::unlimited();
@@ -4057,17 +4067,17 @@ GCRuntime::markWeakReferences(gcstats::P
     MOZ_ASSERT(marker.isDrained());
 
     marker.leaveWeakMarkingMode();
 }
 
 void
 GCRuntime::markWeakReferencesInCurrentGroup(gcstats::Phase phase)
 {
-    markWeakReferences<GCCompartmentGroupIter>(phase);
+    markWeakReferences<GCZoneGroupIter>(phase);
 }
 
 template <class ZoneIterT, class CompartmentIterT>
 void
 GCRuntime::markGrayReferences(gcstats::Phase phase)
 {
     gcstats::AutoPhase ap(stats, phase);
     if (hasBufferedGrayRoots()) {
@@ -4086,17 +4096,17 @@ void
 GCRuntime::markGrayReferencesInCurrentGroup(gcstats::Phase phase)
 {
     markGrayReferences<GCZoneGroupIter, GCCompartmentGroupIter>(phase);
 }
 
 void
 GCRuntime::markAllWeakReferences(gcstats::Phase phase)
 {
-    markWeakReferences<GCCompartmentsIter>(phase);
+    markWeakReferences<GCZonesIter>(phase);
 }
 
 void
 GCRuntime::markAllGrayReferences(gcstats::Phase phase)
 {
     markGrayReferences<GCZonesIter, GCCompartmentsIter>(phase);
 }
 
@@ -4189,28 +4199,29 @@ js::gc::MarkingValidator::nonIncremental
      * Temporarily clear the weakmaps' mark flags for the compartments we are
      * collecting.
      */
 
     WeakMapSet markedWeakMaps;
     if (!markedWeakMaps.init())
         return;
 
-    for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
-        if (!WeakMapBase::saveCompartmentMarkedWeakMaps(c, markedWeakMaps))
+    for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+        if (!WeakMapBase::saveZoneMarkedWeakMaps(zone, markedWeakMaps))
             return;
     }
 
     gc::WeakKeyTable savedWeakKeys;
     if (!savedWeakKeys.init())
         return;
 
     for (gc::WeakKeyTable::Range r = gc->marker.weakKeys.all(); !r.empty(); r.popFront()) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!savedWeakKeys.put(Move(r.front().key), Move(r.front().value)))
-            CrashAtUnhandlableOOM("saving weak keys table for validator");
+            oomUnsafe.crash("saving weak keys table for validator");
     }
 
     /*
      * After this point, the function should run to completion, so we shouldn't
      * do anything fallible.
      */
     initialized = true;
 
@@ -4221,18 +4232,18 @@ js::gc::MarkingValidator::nonIncremental
     gc->incrementalState = MARK_ROOTS;
 
     {
         gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_MARK);
 
         {
             gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_UNMARK);
 
-            for (GCCompartmentsIter c(runtime); !c.done(); c.next())
-                WeakMapBase::unmarkCompartment(c);
+            for (GCZonesIter zone(runtime); !zone.done(); zone.next())
+                WeakMapBase::unmarkZone(zone);
 
             MOZ_ASSERT(gcmarker->isDrained());
             gcmarker->reset();
 
             for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
                 chunk->bitmap.clear();
         }
 
@@ -4271,24 +4282,25 @@ js::gc::MarkingValidator::nonIncremental
 
     /* Take a copy of the non-incremental mark state and restore the original. */
     for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
         ChunkBitmap* bitmap = &chunk->bitmap;
         ChunkBitmap* entry = map.lookup(chunk)->value();
         Swap(*entry, *bitmap);
     }
 
-    for (GCCompartmentsIter c(runtime); !c.done(); c.next())
-        WeakMapBase::unmarkCompartment(c);
+    for (GCZonesIter zone(runtime); !zone.done(); zone.next())
+        WeakMapBase::unmarkZone(zone);
     WeakMapBase::restoreMarkedWeakMaps(markedWeakMaps);
 
     gc->marker.weakKeys.clear();
     for (gc::WeakKeyTable::Range r = savedWeakKeys.all(); !r.empty(); r.popFront()) {
+        AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!gc->marker.weakKeys.put(Move(r.front().key), Move(r.front().value)))
-            CrashAtUnhandlableOOM("restoring weak keys table for validator");
+            oomUnsafe.crash("restoring weak keys table for validator");
     }
 
     gc->incrementalState = state;
 }
 
 void
 js::gc::MarkingValidator::validate()
 {
@@ -4475,18 +4487,18 @@ GCRuntime::findZoneEdgesForWeakMaps()
      * need for zone edges from the delegate's zone to the weakmap zone.
      *
      * Since the edges point into and not away from the zone the weakmap is in
      * we must find these edges in advance and store them in a set on the Zone.
      * If we run out of memory, we fall back to sweeping everything in one
      * group.
      */
 
-    for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) {