Merge m-c to autoland. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 18 Jan 2017 09:59:53 -0500
changeset 377183 280836582bde2dbc2f883ffb85f56cfc65c593b0
parent 377182 5999113009c35768c32afa8dd64f6411c448b05f (current diff)
parent 377162 f94f1552ae0a2ef38684663552603595df863606 (diff)
child 377184 27346172ee35332e28011fc4143de2bce10eaef8
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.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 m-c to autoland. a=merge
gfx/layers/client/CompositableChild.cpp
gfx/layers/client/CompositableChild.h
gfx/layers/ipc/CompositableForwarder.cpp
gfx/layers/ipc/PCompositable.ipdl
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -516,44 +516,16 @@ DocManager::CreateDocOrRootAccessible(ns
     // the tree. The reorder event is delivered after the document tree is
     // constructed because event processing and tree construction are done by
     // the same document.
     // Note: don't use AccReorderEvent to avoid coalsecense and special reorder
     // events processing.
     docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
                              ApplicationAcc());
 
-    if (IPCAccessibilityActive()) {
-      nsIDocShell* docShell = aDocument->GetDocShell();
-      if (docShell) {
-        nsCOMPtr<nsITabChild> tabChild = docShell->GetTabChild();
-
-        // XXX We may need to handle the case that we don't have a tab child
-        // differently.  It may be that this will cause us to fail to notify
-        // the parent process about important accessible documents.
-        if (tabChild) {
-          DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
-          docAcc->SetIPCDoc(ipcDoc);
-
-#if defined(XP_WIN)
-          IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
-#endif
-
-          static_cast<TabChild*>(tabChild.get())->
-            SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
-#if defined(XP_WIN)
-                                          AccessibleWrap::GetChildIDFor(docAcc),
-                                          holder
-#else
-                                          0, 0
-#endif
-                                          );
-        }
-      }
-    }
   } else {
     parentDocAcc->BindChildDocument(docAcc);
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocCreate)) {
     logging::DocCreate("document creation finished", aDocument);
     logging::Stack();
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -41,16 +41,17 @@
 #include "nsIScrollableFrame.h"
 #include "nsUnicharUtils.h"
 #include "nsIURI.h"
 #include "nsIWebNavigation.h"
 #include "nsFocusManager.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/Element.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif
 
 using namespace mozilla;
@@ -1455,18 +1456,35 @@ DocAccessible::NotifyOfLoading(bool aIsR
   RefPtr<AccEvent> stateEvent =
     new AccStateChangeEvent(this, states::BUSY, true);
   FireDelayedEvent(stateEvent);
 }
 
 void
 DocAccessible::DoInitialUpdate()
 {
-  if (nsCoreUtils::IsTabDocument(mDocumentNode))
+  if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
     mDocFlags |= eTabDocument;
+    if (IPCAccessibilityActive()) {
+      nsIDocShell* docShell = mDocumentNode->GetDocShell();
+      if (RefPtr<dom::TabChild> tabChild = dom::TabChild::GetFrom(docShell)) {
+        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this);
+        SetIPCDoc(ipcDoc);
+
+#if defined(XP_WIN)
+        IAccessibleHolder holder(CreateHolderFromAccessible(this));
+        int32_t childID = AccessibleWrap::GetChildIDFor(this);
+#else
+        int32_t holder = 0, childID = 0;
+#endif
+        tabChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0, childID,
+                                                holder);
+      }
+    }
+  }
 
   mLoadState |= eTreeConstructed;
 
   // Set up a root element and ARIA role mapping.
   UpdateRootElIfNeeded();
 
   // Build initial tree.
   CacheChildrenInSubtree(this);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -867,16 +867,22 @@ function _loadURIWithFlags(browser, uri,
       if (params.userContextId) {
         browser.webNavigation.setOriginAttributesBeforeLoading({ userContextId: params.userContextId });
       }
 
       browser.webNavigation.loadURIWithOptions(uri, flags,
                                                referrer, referrerPolicy,
                                                postData, null, null);
     } else {
+      // Check if the current browser is allowed to unload.
+      let {permitUnload, timedOut} = browser.permitUnload();
+      if (!timedOut && !permitUnload) {
+        return;
+      }
+
       if (postData) {
         postData = NetUtil.readInputStreamToString(postData, postData.available());
       }
 
       let loadParams = {
         uri,
         flags,
         referrer: referrer ? referrer.spec : null,
@@ -4355,31 +4361,31 @@ var XULBrowserWindow = {
   // Called before links are navigated to to allow us to retarget them if needed.
   onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
     let target = BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
     SocialUI.closeSocialPanelForLinkTraversal(target, linkNode);
     return target;
   },
 
   // Check whether this URI should load in the current process
-  shouldLoadURI(aDocShell, aURI, aReferrer) {
+  shouldLoadURI(aDocShell, aURI, aReferrer, aTriggeringPrincipal) {
     if (!gMultiProcessBrowser)
       return true;
 
     let browser = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
                            .sameTypeRootTreeItem
                            .QueryInterface(Ci.nsIDocShell)
                            .chromeEventHandler;
 
     // Ignore loads that aren't in the main tabbrowser
     if (browser.localName != "browser" || !browser.getTabBrowser || browser.getTabBrowser() != gBrowser)
       return true;
 
     if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
-      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
+      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
       return false;
     }
 
     return true;
   },
 
   onProgressChange(aWebProgress, aRequest,
                              aCurSelfProgress, aMaxSelfProgress,
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -699,32 +699,32 @@ if (Services.appinfo.processType == Serv
 }
 
 var WebBrowserChrome = {
   onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
     return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
   },
 
   // Check whether this URI should load in the current process
-  shouldLoadURI(aDocShell, aURI, aReferrer) {
+  shouldLoadURI(aDocShell, aURI, aReferrer, aTriggeringPrincipal) {
     if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
-      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
+      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
       return false;
     }
 
     return true;
   },
 
   shouldLoadURIInThisProcess(aURI) {
     return E10SUtils.shouldLoadURIInThisProcess(aURI);
   },
 
   // Try to reload the currently active or currently loading page in a new process.
-  reloadInFreshProcess(aDocShell, aURI, aReferrer) {
-    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, true);
+  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal) {
+    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true);
     return true;
   },
 
   startPrerenderingDocument(aHref, aReferrer) {
     if (PrerenderContentHandler.initialized) {
       PrerenderContentHandler.startPrerenderingDocument(aHref, aReferrer);
     }
   },
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -130,16 +130,19 @@ support-files =
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
   !/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
   !/toolkit/mozapps/extensions/test/xpinstall/restartless-unsigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/restartless.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/theme.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
+  file_about_child.html
+  file_about_parent.html
+  file_register_about_page.js
 
 [browser_aboutAccounts.js]
 skip-if = os == "linux" # Bug 958026
 support-files =
   content_aboutAccounts.js
 [browser_aboutCertError.js]
 [browser_aboutNetError.js]
 [browser_aboutSupport_newtab_security_state.js]
@@ -477,16 +480,17 @@ skip-if = (os == "win" && !debug)
 [browser_zbug569342.js]
 skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
 [browser_registerProtocolHandler_notification.js]
 [browser_no_mcb_on_http_site.js]
 tags = mcb
 [browser_addCertException.js]
 [browser_bug1045809.js]
 tags = mcb
+[browser_e10s_about_page_triggeringprincipal.js]
 [browser_e10s_switchbrowser.js]
 [browser_e10s_about_process.js]
 [browser_e10s_chrome_process.js]
 [browser_e10s_javascript.js]
 [browser_blockHPKP.js]
 tags = psm
 [browser_mcb_redirect.js]
 tags = mcb
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
@@ -0,0 +1,44 @@
+"use strict";
+Cu.import("resource://gre/modules/Services.jsm");
+
+registerCleanupFunction(function() {
+  Services.ppmm.broadcastAsyncMessage("AboutPrincipalTest:Unregister");
+  BrowserTestUtils.waitForMessage(Services.ppmm, "AboutPrincipalTest:Unregistered").then(
+    Services.ppmm.removeDelayedProcessScript(
+      "chrome://mochitests/content/browser/browser/base/content/test/general/file_register_about_page.js"
+    )
+  );
+});
+
+add_task(function* test_principal() {
+  Services.ppmm.loadProcessScript(
+    "chrome://mochitests/content/browser/browser/base/content/test/general/file_register_about_page.js",
+    true
+  );
+
+  yield BrowserTestUtils.withNewTab("about:test-about-principal-parent", function*(browser) {
+    let loadPromise = BrowserTestUtils.browserLoaded(browser, false, "about:test-about-principal-child");
+    let myLink = browser.contentDocument.getElementById("aboutchildprincipal");
+    myLink.click();
+    yield loadPromise;
+
+    yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+      let channel = content.document.docShell.currentDocumentChannel;
+      is(channel.originalURI.asciiSpec,
+         "about:test-about-principal-child",
+         "sanity check - make sure we test the principal for the correct URI");
+
+      let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
+      ok(Services.scriptSecurityManager.isSystemPrincipal(triggeringPrincipal),
+         "loading about: from privileged page must have a triggering of System");
+
+      let contentPolicyType = channel.loadInfo.externalContentPolicyType;
+      is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+        "sanity check - loading a top level document");
+
+      let loadingPrincipal = channel.loadInfo.loadingPrincipal;
+      is(loadingPrincipal, null,
+         "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal");
+    });
+  });
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_about_child.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1329032</title>
+</head>
+<body>
+  Just an about page that only loads in the child!
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_about_parent.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1329032</title>
+</head>
+<body>
+  <a href="about:test-about-principal-child" id="aboutchildprincipal">about:test-about-principal-child</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_register_about_page.js
@@ -0,0 +1,81 @@
+const { interfaces: Ci, results: Cr, manager: Cm, utils: Cu } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function AboutPage(chromeURL, aboutHost, classID, description, uriFlags) {
+  this.chromeURL = chromeURL;
+  this.aboutHost = aboutHost;
+  this.classID = Components.ID(classID);
+  this.description = description;
+  this.uriFlags = uriFlags;
+}
+
+AboutPage.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
+  getURIFlags(aURI) { // eslint-disable-line no-unused-vars
+    return this.uriFlags;
+  },
+
+  newChannel(aURI, aLoadInfo) {
+    let newURI = Services.io.newURI(this.chromeURL);
+    let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
+                                                            aLoadInfo);
+    channel.originalURI = aURI;
+
+    if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
+      channel.owner = null;
+    }
+    return channel;
+  },
+
+  createInstance(outer, iid) {
+    if (outer !== null) {
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    }
+    return this.QueryInterface(iid);
+  },
+
+  register() {
+    Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
+      this.classID, this.description,
+      "@mozilla.org/network/protocol/about;1?what=" + this.aboutHost, this);
+  },
+
+  unregister() {
+    Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(
+      this.classID, this);
+  }
+};
+
+/* exported AboutPrincipalTest */
+var AboutPrincipalTest = {};
+
+XPCOMUtils.defineLazyGetter(AboutPrincipalTest, "aboutChild", () =>
+  new AboutPage("chrome://mochitests/content/browser/browser/base/content/test/general/file_about_child.html",
+                "test-about-principal-child",
+                "{df6cbd19-c95b-4011-874b-315347c0832c}",
+                "About Principal Child Test",
+                Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD |
+                Ci.nsIAboutModule.ALLOW_SCRIPT)
+
+);
+
+XPCOMUtils.defineLazyGetter(AboutPrincipalTest, "aboutParent", () =>
+  new AboutPage("chrome://mochitests/content/browser/browser/base/content/test/general/file_about_parent.html",
+                "test-about-principal-parent",
+                "{15e1a03d-9f94-4352-bfb8-94216140d3ab}",
+                "About Principal Parent Test",
+                Ci.nsIAboutModule.ALLOW_SCRIPT)
+);
+
+AboutPrincipalTest.aboutParent.register();
+AboutPrincipalTest.aboutChild.register();
+
+function onUnregisterMessage() {
+  removeMessageListener("AboutPrincipalTest:Unregister", onUnregisterMessage);
+  AboutPrincipalTest.aboutParent.unregister();
+  AboutPrincipalTest.aboutChild.unregister();
+  sendAsyncMessage("AboutPrincipalTest:Unregistered");
+}
+
+addMessageListener("AboutPrincipalTest:Unregister", onUnregisterMessage);
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -199,24 +199,27 @@ ContentRestoreInternal.prototype = {
         // same state it was before the load started then trigger the load.
         let referrer = loadArguments.referrer ?
                        Utils.makeURI(loadArguments.referrer) : null;
         let referrerPolicy = ('referrerPolicy' in loadArguments
             ? loadArguments.referrerPolicy
             : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
         let postData = loadArguments.postData ?
                        Utils.makeInputStream(loadArguments.postData) : null;
+        let triggeringPrincipal = loadArguments.triggeringPrincipal
+                                  ? Utils.deserializePrincipal(loadArguments.triggeringPrincipal)
+                                  : null;
 
         if (loadArguments.userContextId) {
           webNavigation.setOriginAttributesBeforeLoading({ userContextId: loadArguments.userContextId });
         }
 
         webNavigation.loadURIWithOptions(loadArguments.uri, loadArguments.flags,
                                          referrer, referrerPolicy, postData,
-                                         null, null);
+                                         null, null, triggeringPrincipal);
       } else if (tabData.userTypedValue && tabData.userTypedClear) {
         // If the user typed a URL into the URL bar and hit enter right before
         // we crashed, we want to start loading that page again. A non-zero
         // userTypedClear value means that the load had started.
         // Load userTypedValue and fix up the URL if it's partial/broken.
         webNavigation.loadURI(tabData.userTypedValue,
                               Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
                               null, null, null);
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -8,16 +8,18 @@ this.EXPORTED_SYMBOLS = ["E10SUtils"];
 
 const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
                                       "extensions.webextensions.remote", false);
+XPCOMUtils.defineLazyModuleGetter(this, "Utils",
+                                  "resource://gre/modules/sessionstore/Utils.jsm");
 
 function getAboutModule(aURL) {
   // Needs to match NS_GetAboutModuleName
   let moduleName = aURL.path.replace(/[#?].*/, "").toLowerCase();
   let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
   try {
     return Cc[contract].getService(Ci.nsIAboutModule);
   } catch (e) {
@@ -158,27 +160,30 @@ this.E10SUtils = {
     if (aDocShell.inFreshProcess && aDocShell.isOnlyToplevelInTabGroup) {
       return false;
     }
 
     // If the URI can be loaded in the current process then continue
     return this.shouldLoadURIInThisProcess(aURI);
   },
 
-  redirectLoad(aDocShell, aURI, aReferrer, aFreshProcess) {
+  redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aFreshProcess) {
     // Retarget the load to the correct process
     let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIContentFrameMessageManager);
     let sessionHistory = aDocShell.getInterface(Ci.nsIWebNavigation).sessionHistory;
 
     messageManager.sendAsyncMessage("Browser:LoadURI", {
       loadOptions: {
         uri: aURI.spec,
         flags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
         referrer: aReferrer ? aReferrer.spec : null,
+        triggeringPrincipal: aTriggeringPrincipal
+                             ? Utils.serializePrincipal(aTriggeringPrincipal)
+                             : null,
         reloadInFreshProcess: !!aFreshProcess,
       },
       historyIndex: sessionHistory.requestedIndex,
     });
     return false;
   },
 
   wrapHandlingUserInput(aWindow, aIsHandling, aCallback) {
--- a/devtools/client/responsive.html/browser/web-navigation.js
+++ b/devtools/client/responsive.html/browser/web-navigation.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci, Cu, Cr } = require("chrome");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const Services = require("Services");
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
+const { Utils } = require("resource://gre/modules/sessionstore/Utils.jsm");
 
 function readInputStreamToString(stream) {
   return NetUtil.readInputStreamToString(stream, stream.available());
 }
 
 /**
  * This object aims to provide the nsIWebNavigation interface for mozbrowser elements.
  * nsIWebNavigation is one of the interfaces expected on <xul:browser>s, so this wrapper
@@ -56,30 +57,33 @@ BrowserElementWebNavigation.prototype = 
     // No equivalent in the current BrowserElement API
     this._sendMessage("WebNavigation:GotoIndex", { index });
   },
 
   loadURI(uri, flags, referrer, postData, headers) {
     // No equivalent in the current BrowserElement API
     this.loadURIWithOptions(uri, flags, referrer,
                             Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
-                            postData, headers, null);
+                            postData, headers, null, null);
   },
 
   loadURIWithOptions(uri, flags, referrer, referrerPolicy, postData, headers,
-                     baseURI) {
+                     baseURI, triggeringPrincipal) {
     // No equivalent in the current BrowserElement API
     this._sendMessage("WebNavigation:LoadURI", {
       uri,
       flags,
       referrer: referrer ? referrer.spec : null,
       referrerPolicy: referrerPolicy,
       postData: postData ? readInputStreamToString(postData) : null,
       headers: headers ? readInputStreamToString(headers) : null,
       baseURI: baseURI ? baseURI.spec : null,
+      triggeringPrincipal: triggeringPrincipal
+                           ? Utils.serializePrincipal(triggeringPrincipal)
+                           : null,
     });
   },
 
   setOriginAttributesBeforeLoading(originAttributes) {
     // No equivalent in the current BrowserElement API
     this._sendMessage("WebNavigation:SetOriginAttributes", {
       originAttributes,
     });
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4693,27 +4693,28 @@ NS_IMETHODIMP
 nsDocShell::LoadURI(const char16_t* aURI,
                     uint32_t aLoadFlags,
                     nsIURI* aReferringURI,
                     nsIInputStream* aPostStream,
                     nsIInputStream* aHeaderStream)
 {
   return LoadURIWithOptions(aURI, aLoadFlags, aReferringURI,
                             mozilla::net::RP_Unset, aPostStream,
-                            aHeaderStream, nullptr);
+                            aHeaderStream, nullptr, nullptr);
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadURIWithOptions(const char16_t* aURI,
                                uint32_t aLoadFlags,
                                nsIURI* aReferringURI,
                                uint32_t aReferrerPolicy,
                                nsIInputStream* aPostStream,
                                nsIInputStream* aHeaderStream,
-                               nsIURI* aBaseURI)
+                               nsIURI* aBaseURI,
+                               nsIPrincipal* aTriggeringPrincipal)
 {
   NS_ASSERTION((aLoadFlags & 0xf) == 0, "Unexpected flags");
 
   if (!IsNavigationAllowed()) {
     return NS_OK; // JS may not handle returning of an error code
   }
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIInputStream> postStream(aPostStream);
@@ -4819,16 +4820,17 @@ nsDocShell::LoadURIWithOptions(const cha
   }
 
   loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(loadType));
   loadInfo->SetPostDataStream(postStream);
   loadInfo->SetReferrer(aReferringURI);
   loadInfo->SetReferrerPolicy(aReferrerPolicy);
   loadInfo->SetHeadersStream(aHeaderStream);
   loadInfo->SetBaseURI(aBaseURI);
+  loadInfo->SetTriggeringPrincipal(aTriggeringPrincipal);
 
   if (fixupInfo) {
     nsAutoString searchProvider, keyword;
     fixupInfo->GetKeywordProviderName(searchProvider);
     fixupInfo->GetKeywordAsSent(keyword);
     MaybeNotifyKeywordSearchLoading(searchProvider, keyword);
   }
 
@@ -10462,35 +10464,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
           win->DispatchAsyncHashchange(currentURI, aURI);
         }
       }
 
       return NS_OK;
     }
   }
 
-  // Check if the webbrowser chrome wants the load to proceed; this can be
-  // used to cancel attempts to load URIs in the wrong process.
-  nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
-  if (browserChrome3) {
-    // In case this is a remote newtab load, set aURI to aOriginalURI (newtab).
-    // This ensures that the verifySignedContent flag is set on loadInfo in
-    // DoURILoad.
-    nsIURI* uriForShouldLoadCheck = aURI;
-    if (IsAboutNewtab(aOriginalURI)) {
-      uriForShouldLoadCheck = aOriginalURI;
-    }
-    bool shouldLoad;
-    rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer,
-                                       &shouldLoad);
-    if (NS_SUCCEEDED(rv) && !shouldLoad) {
-      return NS_OK;
-    }
-  }
-
   // mContentViewer->PermitUnload can destroy |this| docShell, which
   // causes the next call of CanSavePresentation to crash.
   // Hold onto |this| until we return, to prevent a crash from happening.
   // (bug#331040)
   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
 
   // Don't init timing for javascript:, since it generally doesn't
   // actually start a load or anything.  If it does, we'll init
@@ -10519,16 +10502,35 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       return NS_OK;
     }
   }
 
   if (mTiming && timeBeforeUnload) {
     mTiming->NotifyUnloadAccepted(mCurrentURI);
   }
 
+  // Check if the webbrowser chrome wants the load to proceed; this can be
+  // used to cancel attempts to load URIs in the wrong process.
+  nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+  if (browserChrome3) {
+    // In case this is a remote newtab load, set aURI to aOriginalURI (newtab).
+    // This ensures that the verifySignedContent flag is set on loadInfo in
+    // DoURILoad.
+    nsIURI* uriForShouldLoadCheck = aURI;
+    if (IsAboutNewtab(aOriginalURI)) {
+      uriForShouldLoadCheck = aOriginalURI;
+    }
+    bool shouldLoad;
+    rv = browserChrome3->ShouldLoadURI(this, uriForShouldLoadCheck, aReferrer,
+                                       aTriggeringPrincipal, &shouldLoad);
+    if (NS_SUCCEEDED(rv) && !shouldLoad) {
+      return NS_OK;
+    }
+  }
+
   if (browserChrome3 && aCheckForPrerender) {
     nsCOMPtr<nsIRunnable> ev =
       new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace,
                             aReferrer, aReferrerPolicy,
                             aTriggeringPrincipal, principalToInherit,
                             aFlags, aTypeHint, aPostData, aHeadersData,
                             aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                             aSourceDocShell, aBaseURI, false);
--- a/docshell/base/nsIWebNavigation.idl
+++ b/docshell/base/nsIWebNavigation.idl
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMDocument;
 interface nsIInputStream;
 interface nsISHistory;
 interface nsIURI;
+interface nsIPrincipal;
 
 /**
  * The nsIWebNavigation interface defines an interface for navigating the web.
  * It provides methods and attributes to direct an object to navigate to a new
  * location, stop or restart an in process load, or determine where the object
  * has previously gone.
  */
 [scriptable, uuid(3ade79d4-8cb9-4952-b18d-4f9b63ca0d31)]
@@ -277,24 +278,30 @@ interface nsIWebNavigation : nsISupports
    *        header stream is formatted as:
    *            ( HEADER "\r\n" )*
    *        This parameter is optional and may be null.
    * @param aBaseURI
    *        Set to indicate a base URI to be associated with the load. Note
    *        that at present this argument is only used with view-source aURIs
    *        and cannot be used to resolve aURI.
    *        This parameter is optional and may be null.
+   * @param aTriggeringPrincipal
+   *        The principal that initiated the load of aURI. If omitted docShell
+   *        tries to create a codeBasePrincipal from aReferrer if not null. If
+   *        aReferrer is also null docShell peforms a load using the
+   *        SystemPrincipal as the triggeringPrincipal.
    */
-  void loadURIWithOptions(in wstring        aURI,
-                          in unsigned long  aLoadFlags,
-                          in nsIURI         aReferrer,
-                          in unsigned long  aReferrerPolicy,
-                          in nsIInputStream aPostData,
-                          in nsIInputStream aHeaders,
-                          in nsIURI         aBaseURI);
+  void loadURIWithOptions(in wstring                  aURI,
+                          in unsigned long            aLoadFlags,
+                          in nsIURI                   aReferrer,
+                          in unsigned long            aReferrerPolicy,
+                          in nsIInputStream           aPostData,
+                          in nsIInputStream           aHeaders,
+                          in nsIURI                   aBaseURI,
+                          [optional] in nsIPrincipal  aTriggeringPrincipal);
 
   /**
    * Tells the Object to reload the current page.  There may be cases where the
    * user will be asked to confirm the reload (for example, when it is
    * determined that the request is non-idempotent).
    *
    * @param aReloadFlags
    *        Flags modifying load behaviour.  This parameter is a bitwise
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -1613,17 +1613,18 @@ nsSHistory::GetSessionHistory(nsISHistor
 
 NS_IMETHODIMP
 nsSHistory::LoadURIWithOptions(const char16_t* aURI,
                                uint32_t aLoadFlags,
                                nsIURI* aReferringURI,
                                uint32_t aReferrerPolicy,
                                nsIInputStream* aPostStream,
                                nsIInputStream* aExtraHeaderStream,
-                               nsIURI* aBaseURI)
+                               nsIURI* aBaseURI,
+                               nsIPrincipal* aTriggeringPrincipal)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::SetOriginAttributesBeforeLoading(JS::HandleValue aOriginAttributes)
 {
   return NS_OK;
--- a/dom/base/Attr.cpp
+++ b/dom/base/Attr.cpp
@@ -272,17 +272,17 @@ Attr::GetBaseURI(bool aTryUseXHRDocBaseU
 {
   Element* parent = GetElement();
 
   return parent ? parent->GetBaseURI(aTryUseXHRDocBaseURI) : nullptr;
 }
 
 void
 Attr::GetTextContentInternal(nsAString& aTextContent,
-                             ErrorResult& aError)
+                             OOMReporter& aError)
 {
   OwnerDoc()->WarnOnceAbout(nsIDocument::eTextContent);
 
   GetValue(aTextContent);
 }
 
 void
 Attr::SetTextContentInternal(const nsAString& aTextContent,
--- a/dom/base/Attr.h
+++ b/dom/base/Attr.h
@@ -39,17 +39,17 @@ public:
        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
        const nsAString& aValue);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsIDOMNode interface
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
   virtual void GetTextContentInternal(nsAString& aTextContent,
-                                      ErrorResult& aError) override;
+                                      OOMReporter& aError) override;
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       ErrorResult& aError) override;
   virtual void GetNodeValueInternal(nsAString& aNodeValue) override;
   virtual void SetNodeValueInternal(const nsAString& aNodeValue,
                                     ErrorResult& aError) override;
 
   // nsIDOMAttr interface
   NS_DECL_NSIDOMATTR
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -9,34 +9,44 @@
 #include "mozilla/Telemetry.h"
 #include "nsIDocShell.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ void
+/* static */ nsresult
 DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
 {
   aKey.Truncate();
   nsCOMPtr<nsIURI> uri;
   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
+    return NS_OK;   // aKey is the empty string
+  }
+
   // GetBaseDomain works fine if |uri| is null, but it outputs a warning
   // which ends up cluttering the logs.
-  if (NS_SUCCEEDED(rv) && uri) {
-    nsCOMPtr<nsIEffectiveTLDService> tldService =
-      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
-    if (tldService) {
-      rv = tldService->GetBaseDomain(uri, 0, aKey);
-      if (NS_FAILED(rv)) {
-        aKey.Truncate();
-      }
-    }
+  if (!uri) {
+    return NS_OK;   // aKey is the empty string
   }
+
+  nsCOMPtr<nsIEffectiveTLDService> tldService =
+    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+  if (!tldService) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = tldService->GetBaseDomain(uri, 0, aKey);
+  if (NS_FAILED(rv)) {
+    aKey.Truncate();
+  }
+
+  return NS_OK;   // aKey may be the empty string
 }
 
 void
 DocGroup::RemoveDocument(nsIDocument* aDocument)
 {
   MOZ_ASSERT(mDocuments.Contains(aDocument));
   mDocuments.RemoveElement(aDocument);
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -39,17 +39,22 @@ class TabGroup;
 class DocGroup final : public Dispatcher
 {
 public:
   typedef nsTArray<nsIDocument*>::iterator Iterator;
   friend class TabGroup;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  static void GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
+  // Returns NS_ERROR_FAILURE and sets |aString| to an empty string if the TLD
+  // service isn't available. Returns NS_OK on success, but may still set
+  // |aString| may still be set to an empty string.
+  static MOZ_MUST_USE nsresult
+  GetKey(nsIPrincipal* aPrincipal, nsACString& aString);
+
   bool MatchesKey(const nsACString& aKey)
   {
     return aKey == mKey;
   }
   TabGroup* GetTabGroup()
   {
     return mTabGroup;
   }
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1156,20 +1156,20 @@ FragmentOrElement::RemoveChildAt(uint32_
 
   if (oldKid) {
     doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
   }
 }
 
 void
 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
-                                          ErrorResult& aError)
+                                          OOMReporter& aError)
 {
   if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
-    aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+    aError.ReportOOM();
   }
 }
 
 void
 FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
                                           ErrorResult& aError)
 {
   aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -119,17 +119,17 @@ public:
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
   virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual void GetTextContentInternal(nsAString& aTextContent,
-                                      mozilla::ErrorResult& aError) override;
+                                      mozilla::OOMReporter& aError) override;
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       mozilla::ErrorResult& aError) override;
 
   // nsIContent interface methods
   virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) override;
   virtual const nsTextFragment *GetText() override;
   virtual uint32_t TextLength() const override;
   virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -13,16 +13,17 @@
 
 namespace mozilla {
 namespace dom {
 
 Timeout::Timeout()
   : mCleared(false),
     mRunning(false),
     mIsInterval(false),
+    mIsTracking(false),
     mReason(Reason::eTimeoutOrInterval),
     mTimeoutId(0),
     mInterval(0),
     mFiringDepth(0),
     mNestingLevel(0),
     mPopupState(openAllowed)
 {
   MOZ_COUNT_CTOR(Timeout);
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -68,16 +68,19 @@ public:
   bool mCleared;
 
   // True if this is one of the timeouts that are currently running
   bool mRunning;
 
   // True if this is a repeating/interval timer
   bool mIsInterval;
 
+  // True if this is a timeout coming from a tracking script
+  bool mIsTracking;
+
   Reason mReason;
 
   // Returned as value of setTimeout()
   uint32_t mTimeoutId;
 
   // Interval in milliseconds
   uint32_t mInterval;
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -15,28 +15,35 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static int32_t              gRunningTimeoutDepth       = 0;
 
 // The default shortest interval/timeout we permit
 #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
 #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
-static int32_t gMinTimeoutValue;
-static int32_t gMinBackgroundTimeoutValue;
+#define DEFAULT_MIN_TRACKING_TIMEOUT_VALUE 4 // 4ms
+#define DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
+static int32_t gMinTimeoutValue = 0;
+static int32_t gMinBackgroundTimeoutValue = 0;
+static int32_t gMinTrackingTimeoutValue = 0;
+static int32_t gMinTrackingBackgroundTimeoutValue = 0;
 int32_t
-TimeoutManager::DOMMinTimeoutValue() const {
+TimeoutManager::DOMMinTimeoutValue(bool aIsTracking) const {
   // First apply any back pressure delay that might be in effect.
   int32_t value = std::max(mBackPressureDelayMS, 0);
   // Don't use the background timeout value when there are audio contexts
   // present, so that background audio can keep running smoothly. (bug 1181073)
   bool isBackground = !mWindow.AsInner()->HasAudioContexts() &&
     mWindow.IsBackgroundInternal();
-  return
-    std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value);
+  auto minValue = aIsTracking ? (isBackground ? gMinTrackingBackgroundTimeoutValue
+                                              : gMinTrackingTimeoutValue)
+                              : (isBackground ? gMinBackgroundTimeoutValue
+                                              : gMinTimeoutValue);
+  return std::max(minValue, value);
 }
 
 #define TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY 0 // Consider all timeouts coming from tracking scripts as tracking
 // These strategies are useful for testing.
 #define ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY        1 // Consider all timeouts as normal
 #define ALTERNATE_TIMEOUT_BUCKETING_STRATEGY         2 // Put every other timeout in the list of tracking timeouts
 #define RANDOM_TIMEOUT_BUCKETING_STRATEGY            3 // Put timeouts into either the normal or tracking timeouts list randomly
 static int32_t gTimeoutBucketingStrategy = 0;
@@ -115,16 +122,22 @@ void
 TimeoutManager::Initialize()
 {
   Preferences::AddIntVarCache(&gMinTimeoutValue,
                               "dom.min_timeout_value",
                               DEFAULT_MIN_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
                               "dom.min_background_timeout_value",
                               DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
+  Preferences::AddIntVarCache(&gMinTrackingTimeoutValue,
+                              "dom.min_tracking_timeout_value",
+                              DEFAULT_MIN_TRACKING_TIMEOUT_VALUE);
+  Preferences::AddIntVarCache(&gMinTrackingBackgroundTimeoutValue,
+                              "dom.min_tracking_background_timeout_value",
+                              DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
                               "dom.timeout_bucketing_strategy",
                               TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
 }
 
 uint32_t
 TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
 {
@@ -162,24 +175,46 @@ TimeoutManager::SetTimeout(nsITimeoutHan
   }
 
   RefPtr<Timeout> timeout = new Timeout();
   timeout->mIsInterval = aIsInterval;
   timeout->mInterval = interval;
   timeout->mScriptHandler = aHandler;
   timeout->mReason = aReason;
 
+  switch (gTimeoutBucketingStrategy) {
+  default:
+  case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: {
+    const char* filename = nullptr;
+    uint32_t dummyLine = 0, dummyColumn = 0;
+    aHandler->GetLocation(&filename, &dummyLine, &dummyColumn);
+    timeout->mIsTracking = doc->IsScriptTracking(nsDependentCString(filename));
+    break;
+  }
+  case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY:
+    // timeout->mIsTracking is already false!
+    MOZ_DIAGNOSTIC_ASSERT(!timeout->mIsTracking);
+    break;
+  case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY:
+    timeout->mIsTracking = (mTimeoutIdCounter % 2) == 0;
+    break;
+  case RANDOM_TIMEOUT_BUCKETING_STRATEGY:
+    timeout->mIsTracking = (rand() % 2) == 0;
+    break;
+  }
+
   // Now clamp the actual interval we will use for the timer based on
   uint32_t nestingLevel = sNestingLevel + 1;
   uint32_t realInterval = interval;
   if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL ||
       mBackPressureDelayMS > 0 || mWindow.IsBackgroundInternal()) {
     // Don't allow timeouts less than DOMMinTimeoutValue() from
     // now...
-    realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
+    realInterval = std::max(realInterval,
+                            uint32_t(DOMMinTimeoutValue(timeout->mIsTracking)));
   }
 
   timeout->mWindow = &mWindow;
 
   TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
   timeout->SetWhenOrTimeRemaining(TimeStamp::Now(), delta);
 
   // If we're not suspended, then set the timer.
@@ -224,40 +259,19 @@ TimeoutManager::SetTimeout(nsITimeoutHan
     // This is checking |interval|, not realInterval, on purpose,
     // because our lower bound for |realInterval| could be pretty high
     // in some cases.
     if (interval <= delay) {
       timeout->mPopupState = mWindow.GetPopupControlState();
     }
   }
 
-  bool isTracking = false;
-  switch (gTimeoutBucketingStrategy) {
-  default:
-  case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: {
-    const char* filename = nullptr;
-    uint32_t dummyLine = 0, dummyColumn = 0;
-    aHandler->GetLocation(&filename, &dummyLine, &dummyColumn);
-    isTracking = doc->IsScriptTracking(nsDependentCString(filename));
-    break;
-  }
-  case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY:
-    // isTracking is already false!
-    break;
-  case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY:
-    isTracking = (mTimeoutIdCounter % 2) == 0;
-    break;
-  case RANDOM_TIMEOUT_BUCKETING_STRATEGY:
-    isTracking = (rand() % 2) == 0;
-    break;
-  }
-
   Timeouts::SortBy sort(mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
                                            : Timeouts::SortBy::TimeWhen);
-  if (isTracking) {
+  if (timeout->mIsTracking) {
     mTrackingTimeouts.Insert(timeout, sort);
   } else {
     mNormalTimeouts.Insert(timeout, sort);
   }
 
   timeout->mTimeoutId = GetTimeoutId(aReason);
   *aReturn = timeout->mTimeoutId;
 
@@ -675,18 +689,19 @@ TimeoutManager::RescheduleTimeout(Timeou
       aTimeout->Release();
     }
     return false;
   }
 
   // Compute time to next timeout for interval timer.
   // Make sure nextInterval is at least DOMMinTimeoutValue().
   TimeDuration nextInterval =
-    TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval,
-                                          uint32_t(DOMMinTimeoutValue())));
+    TimeDuration::FromMilliseconds(
+        std::max(aTimeout->mInterval,
+                 uint32_t(DOMMinTimeoutValue(aTimeout->mIsTracking))));
 
   // If we're running pending timeouts, set the next interval to be
   // relative to "now", and not to when the timeout that was pending
   // should have fired.
   TimeStamp firingTime;
   if (aRunningPendingTimeouts) {
     firingTime = now + nextInterval;
   } else {
@@ -747,38 +762,37 @@ nsresult
 TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS)
 {
   MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
 
   if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
     return NS_OK;
   }
 
-  auto minTimeout = DOMMinTimeoutValue();
   Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
                                                : Timeouts::SortBy::TimeWhen;
 
   nsCOMPtr<nsIEventTarget> queue = mWindow.EventTargetFor(TaskCategory::Timer);
   nsresult rv = mNormalTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
-                                                                minTimeout,
+                                                                *this,
                                                                 sortBy,
                                                                 queue);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mTrackingTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
-                                                         minTimeout,
+                                                         *this,
                                                          sortBy,
                                                          queue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
-                                                          int32_t aMinTimeoutValueMS,
+                                                          const TimeoutManager& aTimeoutManager,
                                                           SortBy aSortBy,
                                                           nsIEventTarget* aQueue)
 {
   TimeStamp now = TimeStamp::Now();
 
   // If insertion point is non-null, we're in the middle of firing timers and
   // the timers we're planning to fire all come before insertion point;
   // insertion point itself is a dummy timeout with an When() that may be
@@ -804,18 +818,20 @@ TimeoutManager::Timeouts::ResetTimersFor
       // gMinBackgroundTimeoutValue ms and hence were not clamped.
       break;
     }
 
     // We reduced our throttled delay. Re-init the timer appropriately.
     // Compute the interval the timer should have had if it had not been set in a
     // background window
     TimeDuration interval =
-      TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
-                                            uint32_t(aMinTimeoutValueMS)));
+      TimeDuration::FromMilliseconds(
+          std::max(timeout->mInterval,
+                   uint32_t(aTimeoutManager.
+                                DOMMinTimeoutValue(timeout->mIsTracking))));
     uint32_t oldIntervalMillisecs = 0;
     timeout->mTimer->GetDelay(&oldIntervalMillisecs);
     TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
     if (oldInterval > interval) {
       // unclamp
       TimeStamp firingTime =
         std::max(timeout->When() - oldInterval + interval, now);
 
@@ -1009,17 +1025,17 @@ TimeoutManager::Resume()
     // the deadline has already passed or falls within our minimum delay
     // deadline, then clamp the resulting value to the minimum delay.  The
     // When() will remain at its absolute time, but we won'aTimeout fire the OS
     // timer until our calculated delay has passed.
     int32_t remaining = 0;
     if (aTimeout->When() > now) {
       remaining = static_cast<int32_t>((aTimeout->When() - now).ToMilliseconds());
     }
-    uint32_t delay = std::max(remaining, DOMMinTimeoutValue());
+    uint32_t delay = std::max(remaining, DOMMinTimeoutValue(aTimeout->mIsTracking));
 
     aTimeout->mTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (!aTimeout->mTimer) {
       aTimeout->remove();
       return;
     }
 
     nsresult rv = aTimeout->InitTimer(mWindow.EventTargetFor(TaskCategory::Timer),
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -62,17 +62,17 @@ public:
   // state.  If the queue has drained back pressure may be canceled.
   void CancelOrUpdateBackPressure(nsGlobalWindow* aWindow);
 
   // When timers are being throttled and we reduce the thottle delay we must
   // reschedule.  The amount of the old throttle delay must be provided in
   // order to bound how many timers must be examined.
   nsresult ResetTimersForThrottleReduction();
 
-  int32_t DOMMinTimeoutValue() const;
+  int32_t DOMMinTimeoutValue(bool aIsTracking) const;
 
   // aTimeout is the timeout that we're about to start running.  This function
   // returns the current timeout.
   mozilla::dom::Timeout* BeginRunningTimeout(mozilla::dom::Timeout* aTimeout);
   // aTimeout is the last running timeout.
   void EndRunningTimeout(mozilla::dom::Timeout* aTimeout);
 
   void UnmarkGrayTimers();
@@ -124,17 +124,17 @@ private:
     // fire after it, but no earlier than mTimeoutInsertionPoint, if any.
     enum class SortBy
     {
       TimeRemaining,
       TimeWhen
     };
     void Insert(mozilla::dom::Timeout* aTimeout, SortBy aSortBy);
     nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
-                                             int32_t aMinTimeoutValueMS,
+                                             const TimeoutManager& aTimeoutManager,
                                              SortBy aSortBy,
                                              nsIEventTarget* aQueue);
 
     const Timeout* GetFirst() const { return mTimeoutList.getFirst(); }
     Timeout* GetFirst() { return mTimeoutList.getFirst(); }
     const Timeout* GetLast() const { return mTimeoutList.getLast(); }
     Timeout* GetLast() { return mTimeoutList.getLast(); }
     bool IsEmpty() const { return mTimeoutList.isEmpty(); }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9771,19 +9771,23 @@ nsContentUtils::AttemptLargeAllocationLo
   rv = aChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, false);
   NS_ENSURE_TRUE(uri, false);
 
   nsCOMPtr<nsIURI> referrer;
   rv = aChannel->GetReferrer(getter_AddRefs(referrer));
   NS_ENSURE_SUCCESS(rv, false);
 
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
+
   // Actually perform the cross process load
   bool reloadSucceeded = false;
-  rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer, &reloadSucceeded);
+  rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer,
+                                  triggeringPrincipal, &reloadSucceeded);
   NS_ENSURE_SUCCESS(rv, false);
 
   return reloadSucceeded;
 }
 
 /* static */ void
 nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
     nsIDocument* aDocument,
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2862,18 +2862,22 @@ nsDocument::SetPrincipal(nsIPrincipal *a
 
 mozilla::dom::DocGroup*
 nsIDocument::GetDocGroup() const
 {
 #ifdef DEBUG
   // Sanity check that we have an up-to-date and accurate docgroup
   if (mDocGroup) {
     nsAutoCString docGroupKey;
-    mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
-    MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+
+    // GetKey() can fail, e.g. after the TLD service has shut down.
+    nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
+    if (NS_SUCCEEDED(rv)) {
+      MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+    }
     // XXX: Check that the TabGroup is correct as well!
   }
 #endif
 
   return mDocGroup;
 }
 
 nsresult
@@ -4196,20 +4200,23 @@ nsDocument::SetStyleSheetApplicableState
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
                                "StyleSheetApplicableStateChanged",
                                mApplicable,
                                aApplicable);
   }
 
   if (!mSSApplicableStateNotificationPending) {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(this,
       &nsDocument::NotifyStyleSheetApplicableStateChanged);
     mSSApplicableStateNotificationPending =
-      NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
+      NS_SUCCEEDED(
+        Dispatch("nsDocument::NotifyStyleSheetApplicableStateChanged",
+                 TaskCategory::Other, notification.forget()));
   }
 }
 
 void
 nsDocument::NotifyStyleSheetApplicableStateChanged()
 {
   mSSApplicableStateNotificationPending = false;
   nsCOMPtr<nsIObserverService> observerService =
@@ -4372,19 +4379,22 @@ nsDocument::SetScopeObject(nsIGlobalObje
     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
     if (window) {
       // We want to get the tabgroup unconditionally, such that we can make
       // certain that it is cached in the inner window early enough.
       mozilla::dom::TabGroup* tabgroup = window->TabGroup();
       // We should already have the principal, and now that we have been added to a
       // window, we should be able to join a DocGroup!
       nsAutoCString docGroupKey;
-      mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
+      nsresult rv =
+        mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
       if (mDocGroup) {
-        MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+        if (NS_SUCCEEDED(rv)) {
+          MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
+        }
       } else {
         mDocGroup = tabgroup->AddDocument(docGroupKey, this);
         MOZ_ASSERT(mDocGroup);
       }
     }
   }
 }
 
@@ -6747,20 +6757,21 @@ nsDocument::NotifyPossibleTitleChange(bo
   }
 
   if (aBoundTitleElement) {
     mMayHaveTitleElement = true;
   }
   if (mPendingTitleChangeEvent.IsPending())
     return;
 
-  RefPtr<nsRunnableMethod<nsDocument, void, false> > event =
-    NewNonOwningRunnableMethod(this,
-      &nsDocument::DoNotifyPossibleTitleChange);
-  nsresult rv = NS_DispatchToCurrentThread(event);
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  RefPtr<nsRunnableMethod<nsDocument, void, false>> event =
+    NewNonOwningRunnableMethod(this, &nsDocument::DoNotifyPossibleTitleChange);
+  nsresult rv = Dispatch("nsDocument::DoNotifyPossibleTitleChange",
+                         TaskCategory::Other, do_AddRef(event));
   if (NS_SUCCEEDED(rv)) {
     mPendingTitleChangeEvent = event;
   }
 }
 
 void
 nsDocument::DoNotifyPossibleTitleChange()
 {
@@ -8620,18 +8631,20 @@ public:
   }
 private:
   RefPtr<nsDocument> mDoc;
 };
 
 void
 nsDocument::PostUnblockOnloadEvent()
 {
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
-  nsresult rv = NS_DispatchToCurrentThread(evt);
+  nsresult rv =
+    Dispatch("nsUnblockOnloadEvent", TaskCategory::Other, evt.forget());
   if (NS_SUCCEEDED(rv)) {
     // Stabilize block count so we don't post more events while this one is up
     ++mOnloadBlockCount;
   } else {
     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
   }
 }
 
@@ -9551,17 +9564,19 @@ nsDocument::UnsuppressEventHandlingAndFi
   GetAndUnsuppressSubDocuments(this, &args);
 
   if (aWhat == nsIDocument::eAnimationsOnly) {
     // No need to fire events if we only care about animations here.
     return;
   }
 
   if (aFireEvents) {
-    NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs));
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(args.mDocs);
+    Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget());
   } else {
     FireOrClearDelayedEvents(args.mDocs, false);
   }
 }
 
 nsISupports*
 nsDocument::GetCurrentContentSink()
 {
@@ -10575,17 +10590,23 @@ public:
 
 private:
   nsCOMPtr<nsIDocument> mDoc;
 };
 
 /* static */ void
 nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
 {
-  NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
+  if (aDoc) {
+    aDoc->Dispatch("nsCallExitFullscreen", TaskCategory::Other, exit.forget());
+  } else {
+    NS_DispatchToCurrentThread(exit.forget());
+  }
 }
 
 static bool
 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
 {
   if (aDoc->GetFullscreenElement()) {
     uint32_t* count = static_cast<uint32_t*>(aData);
     (*count)++;
@@ -10845,18 +10866,19 @@ nsDocument::AsyncRequestFullScreen(Uniqu
 {
   if (!aRequest->GetElement()) {
     MOZ_ASSERT_UNREACHABLE(
       "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
     return;
   }
 
   // Request full-screen asynchronously.
-  nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(Move(aRequest)));
-  NS_DispatchToCurrentThread(event);
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIRunnable> event = new nsCallRequestFullScreen(Move(aRequest));
+  Dispatch("nsCallRequestFullScreen", TaskCategory::Other, event.forget());
 }
 
 void
 nsIDocument::DispatchFullscreenError(const char* aMessage)
 {
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this,
                              NS_LITERAL_STRING("fullscreenerror"),
@@ -11720,18 +11742,19 @@ nsDocument::RequestPointerLock(Element* 
 
   if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) {
     DispatchPointerLockError(this, msg);
     return;
   }
 
   bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
                                  aCallerType == CallerType::System;
-  NS_DispatchToMainThread(new PointerLockRequest(aElement,
-                                                 userInputOrSystemCaller));
+  nsCOMPtr<nsIRunnable> request =
+    new PointerLockRequest(aElement, userInputOrSystemCaller);
+  Dispatch("PointerLockRequest", TaskCategory::Other, request.forget());
 }
 
 bool
 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
 {
   MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
              "We should be either unlocking pointer (aElement is nullptr), "
              "or locking pointer to an element in this document");
@@ -12445,19 +12468,21 @@ nsDocument::UpdateIntersectionObservatio
   for (const auto& observer : mIntersectionObservers) {
     observer->Update(this, time);
   }
 }
 
 void
 nsDocument::ScheduleIntersectionObserverNotification()
 {
-  nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(this,
-    &nsDocument::NotifyIntersectionObservers);
-  NS_DispatchToCurrentThread(notification);
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIRunnable> notification =
+    NewRunnableMethod(this, &nsDocument::NotifyIntersectionObservers);
+  Dispatch("nsDocument::IntersectionObserverNotification", TaskCategory::Other,
+           notification.forget());
 }
 
 void
 nsDocument::NotifyIntersectionObservers()
 {
   nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers);
   for (const auto& observer : observers) {
     observer->Notify();
@@ -12729,19 +12754,21 @@ nsIDocument::RebuildUserFontSet()
   // Somebody has already asked for the user font set, so we need to
   // post an event to rebuild it.  Setting the user font set to be dirty
   // and lazily rebuilding it isn't sufficient, since it is only the act
   // of rebuilding it that will trigger the style change reflow that
   // calls GetUserFontSet.  (This reflow causes rebuilding of text runs,
   // which starts font loads, whose completion causes another style
   // change reflow).
   if (!mPostedFlushUserFontSet) {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet);
-    if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
+    if (NS_SUCCEEDED(Dispatch("nsIDocument::HandleRebuildUserFontSet",
+                              TaskCategory::Other, ev.forget()))) {
       mPostedFlushUserFontSet = true;
     }
   }
 }
 
 FontFaceSet*
 nsIDocument::Fonts()
 {
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -84,17 +84,17 @@ public:
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt(uint32_t aIndex) const override;
   virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override;
   virtual int32_t IndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex,
                                  bool aNotify) override;
   virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override;
   virtual void GetTextContentInternal(nsAString& aTextContent,
-                                      mozilla::ErrorResult& aError) override
+                                      mozilla::OOMReporter& aError) override
   {
     GetNodeValue(aTextContent);
   }
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       mozilla::ErrorResult& aError) override
   {
     // Batch possible DOMSubtreeModified events.
     mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8599,17 +8599,18 @@ class nsCloseEvent : public Runnable {
     , mIndirect(aIndirect)
   {}
 
 public:
 
   static nsresult
   PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
     nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
-    nsresult rv = NS_DispatchToCurrentThread(ev);
+    nsresult rv =
+      aWindow->Dispatch("nsCloseEvent", TaskCategory::Other, ev.forget());
     if (NS_SUCCEEDED(rv))
       aWindow->MaybeForgiveSpamCount();
     return rv;
   }
 
   NS_IMETHOD Run() override {
     if (mWindow) {
       if (mIndirect) {
@@ -9078,17 +9079,18 @@ private:
   nsCString mTopic;
   nsWeakPtr mWindow;
 };
 
 void
 nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic)
 {
   nsCOMPtr<nsIRunnable> runnable = new WindowDestroyedEvent(this, mWindowID, aTopic);
-  nsresult rv = NS_DispatchToCurrentThread(runnable);
+  nsresult rv =
+    Dispatch("WindowDestroyedEvent", TaskCategory::Other, runnable.forget());
   if (NS_SUCCEEDED(rv)) {
     mNotifiedIDDestroyed = true;
   }
 }
 
 // static
 void
 nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) {
@@ -10341,17 +10343,17 @@ nsGlobalWindow::DispatchAsyncHashchange(
   rv = aNewURI->GetSpec(newSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
   NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
 
   nsCOMPtr<nsIRunnable> callback =
     new HashchangeCallback(oldWideSpec, newWideSpec, this);
-  return NS_DispatchToMainThread(callback);
+  return Dispatch("HashchangeCallback", TaskCategory::Other, callback.forget());
 }
 
 nsresult
 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
                                const nsAString &aNewURL)
 {
   MOZ_ASSERT(IsInnerWindow());
 
@@ -10945,17 +10947,18 @@ nsGlobalWindow::NotifyIdleObserver(IdleO
 {
   MOZ_ASSERT(aIdleObserverHolder);
   aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle;
 
   nsCOMPtr<nsIRunnable> caller =
     new NotifyIdleObserverRunnable(aIdleObserverHolder->mIdleObserver,
                                    aIdleObserverHolder->mTimeInS,
                                    aCallOnidle, this);
-  if (NS_FAILED(NS_DispatchToCurrentThread(caller))) {
+  if (NS_FAILED(Dispatch("NotifyIdleObserverRunnable", TaskCategory::Other,
+                         caller.forget()))) {
     NS_WARNING("Failed to dispatch thread for idle observer notification.");
   }
 }
 
 bool
 nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t aTimeInS)
 {
   MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
@@ -12141,17 +12144,19 @@ public:
     : mWin(aWin)
   {
     MOZ_ASSERT(mWin);
     MOZ_ASSERT(mWin->IsOuterWindow());
   }
   ~AutoUnblockScriptClosing()
   {
     void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
-    NS_DispatchToCurrentThread(NewRunnableMethod(mWin, run));
+    nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(mWin, run);
+    mWin->Dispatch("nsGlobalWindow::UnblockScriptedClosing",
+                   TaskCategory::Other, caller.forget());
   }
 };
 
 nsresult
 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
                              const nsAString& aOptions, bool aDialog,
                              bool aContentModal, bool aCalledNoScript,
                              bool aDoJSFixups, bool aNavigate,
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -405,17 +405,17 @@ nsINode::ChildNodes()
   if (!slots->mChildNodes) {
     slots->mChildNodes = new nsChildContentList(this);
   }
 
   return slots->mChildNodes;
 }
 
 void
-nsINode::GetTextContentInternal(nsAString& aTextContent, ErrorResult& aError)
+nsINode::GetTextContentInternal(nsAString& aTextContent, OOMReporter& aError)
 {
   SetDOMStringToNull(aTextContent);
 }
 
 nsIDocument*
 nsINode::GetComposedDocInternal() const
 {
   MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE) && IsContent(),
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1304,17 +1304,17 @@ protected:
     if (HasExplicitBaseURI()) {
       return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty));
     }
     return nullptr;
   }
 
 public:
   void GetTextContent(nsAString& aTextContent,
-                      mozilla::ErrorResult& aError)
+                      mozilla::OOMReporter& aError)
   {
     GetTextContentInternal(aTextContent, aError);
   }
   void SetTextContent(const nsAString& aTextContent,
                       mozilla::ErrorResult& aError)
   {
     SetTextContentInternal(aTextContent, aError);
   }
@@ -1925,17 +1925,17 @@ protected:
 
   bool IsEditableInternal() const;
   virtual bool IsEditableExternal() const
   {
     return IsEditableInternal();
   }
 
   virtual void GetTextContentInternal(nsAString& aTextContent,
-                                      mozilla::ErrorResult& aError);
+                                      mozilla::OOMReporter& aError);
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       mozilla::ErrorResult& aError)
   {
   }
 
 #ifdef DEBUG
   // Note: virtual so that IsInNativeAnonymousSubtree can be called accross
   // module boundaries.
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1847,26 +1847,16 @@ HasPropertyOnPrototype(JSContext* cx, JS
 // shadowPrototypeProperties is false then skip properties that are also
 // present on the proto chain of proxy.  If shadowPrototypeProperties is true,
 // then the "proxy" argument is ignored.
 bool
 AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
                        nsTArray<nsString>& names,
                        bool shadowPrototypeProperties, JS::AutoIdVector& props);
 
-namespace binding_detail {
-
-class FastErrorResult :
-    public mozilla::binding_danger::TErrorResult<
-      mozilla::binding_danger::JustAssertCleanupPolicy>
-{
-};
-
-} // namespace binding_detail
-
 enum StringificationBehavior {
   eStringify,
   eEmpty,
   eNull
 };
 
 template<typename T>
 static inline bool
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7076,17 +7076,18 @@ class CGCallGenerator(CGThing):
                 args.append(CGGeneric("&" + resultVar))
 
         if needsSubjectPrincipal:
             args.append(CGGeneric("subjectPrincipal"))
 
         if needsCallerType:
             args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
 
-        if isFallible:
+        canOOM = "canOOM" in extendedAttributes
+        if isFallible or canOOM:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
         call = CGGeneric(nativeMethodName)
         if not static:
@@ -7142,18 +7143,22 @@ class CGCallGenerator(CGThing):
                     """
                     $*{getPrincipal}
                     // Initializing a nonnull is pretty darn annoying...
                     NonNull<nsIPrincipal> subjectPrincipal;
                     subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
                     """,
                     getPrincipal=getPrincipal)))
 
-        if isFallible:
-            self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
+        if isFallible or canOOM:
+            if isFallible:
+                reporterClass = "binding_detail::FastErrorResult"
+            else:
+                reporterClass = "binding_danger::OOMReporterInstantiator"
+            self.cgRoot.prepend(CGGeneric("%s rv;\n" % reporterClass))
             self.cgRoot.append(CGGeneric(dedent(
                 """
                 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
                   return false;
                 }
                 """)))
 
         self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
@@ -8926,19 +8931,20 @@ class CGSpecializedGetter(CGAbstractStat
                              self.descriptor, self.attr).define())
 
     @staticmethod
     def makeNativeName(descriptor, attr):
         name = attr.identifier.name
         nativeName = MakeNativeName(descriptor.binaryNameFor(name))
         _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type,
                                                                  descriptor)
-        infallible = ('infallible' in
-                      descriptor.getExtendedAttributes(attr, getter=True))
-        if resultOutParam or attr.type.nullable() or not infallible:
+        extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
+        canFail = ('infallible' not in extendedAttrs or
+                   'canOOM' in extendedAttrs)
+        if resultOutParam or attr.type.nullable() or canFail:
             nativeName = "Get" + nativeName
         return nativeName
 
 
 class CGStaticGetter(CGAbstractStaticBindingMethod):
     """
     A class for generating the C++ code for an IDL static attribute getter.
     """
@@ -9226,23 +9232,36 @@ class CGMemberJITInfo(CGThing):
     def define(self):
         if self.member.isAttr():
             getterinfo = ("%s_getterinfo" %
                           IDLToCIdentifier(self.member.identifier.name))
             # We need the cast here because JSJitGetterOp has a "void* self"
             # while we have the right type.
             getter = ("(JSJitGetterOp)get_%s" %
                       IDLToCIdentifier(self.member.identifier.name))
-            getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
-
+            extendedAttrs = self.descriptor.getExtendedAttributes(self.member, getter=True)
+            getterinfal = "infallible" in extendedAttrs
+
+            # At this point getterinfal is true if our getter either can't throw
+            # at all, or can only throw OOM.  In both cases, it's safe to move,
+            # or dead-code-eliminate, the getter, because throwing OOM is not
+            # semantically meaningful, so code can't rely on it happening.  Note
+            # that this makes the behavior consistent for OOM thrown from the
+            # getter itself and OOM thrown from the to-JS conversion of the
+            # return value (see the "canOOM" and "infallibleForMember" checks
+            # below).
             movable = self.mayBeMovable() and getterinfal
             eliminatable = self.mayBeEliminatable() and getterinfal
             aliasSet = self.aliasSet()
 
-            getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+            # Now we have to set getterinfal to whether we can _really_ ever
+            # throw, from the point of view of the JS engine.
+            getterinfal = (getterinfal and
+                           "canOOM" not in extendedAttrs and
+                           infallibleForMember(self.member, self.member.type, self.descriptor))
             isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
             if self.member.slotIndices is not None:
                 assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
                 isLazilyCachedInSlot = not isAlwaysInSlot
                 slotIndex = memberReservedSlot(self.member, self.descriptor)
                 # We'll statically assert that this is not too big in
                 # CGUpdateMemberSlotsMethod, in the case when
                 # isAlwaysInSlot is true.
@@ -9295,27 +9314,37 @@ class CGMemberJITInfo(CGThing):
                 eliminatable = False
             else:
                 sig = sigs[0]
                 # For methods that affect nothing, it's OK to set movable to our
                 # notion of infallible on the C++ side, without considering
                 # argument conversions, since argument conversions that can
                 # reliably throw would be effectful anyway and the jit doesn't
                 # move effectful things.
-                hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
+                extendedAttrs = self.descriptor.getExtendedAttributes(self.member)
+                hasInfallibleImpl = "infallible" in extendedAttrs
+                # At this point hasInfallibleImpl is true if our method either
+                # can't throw at all, or can only throw OOM.  In both cases, it
+                # may be safe to move, or dead-code-eliminate, the method,
+                # because throwing OOM is not semantically meaningful, so code
+                # can't rely on it happening.  Note that this makes the behavior
+                # consistent for OOM thrown from the method itself and OOM
+                # thrown from the to-JS conversion of the return value (see the
+                # "canOOM" and "infallibleForMember" checks below).
                 movable = self.mayBeMovable() and hasInfallibleImpl
                 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
                 # XXXbz can we move the smarts about fallibility due to arg
                 # conversions into the JIT, using our new args stuff?
                 if (len(sig[1]) != 0 or
                     not infallibleForMember(self.member, sig[0], self.descriptor)):
                     # We have arguments or our return-value boxing can fail
                     methodInfal = False
                 else:
-                    methodInfal = hasInfallibleImpl
+                    methodInfal = (hasInfallibleImpl and
+                                   "canOOM" not in extendedAttrs)
                 # For now, only bother to output args if we're side-effect-free.
                 if self.member.affects == "Nothing":
                     args = sig[1]
                 else:
                     args = None
 
             aliasSet = self.aliasSet()
             result = self.defineJitInfo(methodinfo, method, "Method",
@@ -14177,20 +14206,23 @@ class CGNativeMember(ClassMethod):
             # Cheat and assume self.descriptorProvider is a descriptor
             if self.descriptorProvider.interface.isExposedInAnyWorker():
                 args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal"))
             else:
                 args.append(Argument("nsIPrincipal&", "aPrincipal"))
         # And the caller type, if desired.
         if needsCallerType(self.member):
             args.append(Argument("CallerType", "aCallerType"))
-        # And the ErrorResult
+        # And the ErrorResult or OOMReporter
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
+        elif 'canOOM' in self.extendedAttrs:
+            args.append(Argument("OOMReporter&", "aRv"))
+
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
             args.insert(0, Argument("const JS::Value&", "aThisVal"))
         # And jscontext bits.
         if needCx(returnType, argList, self.extendedAttrs,
                   self.passJSBitsAsNeeded, self.member.isStatic()):
@@ -16863,16 +16895,18 @@ class CGEventGetter(CGNativeMember):
                                 (attr.type, []),
                                 ea,
                                 resultNotAddRefed=not attr.type.isSequence())
         self.body = self.getMethodBody()
 
     def getArgs(self, returnType, argList):
         if 'infallible' not in self.extendedAttrs:
             raise TypeError("Event code generator does not support [Throws]!")
+        if 'canOOM' in self.extendedAttrs:
+            raise TypeError("Event code generator does not support [CanOOM]!")
         if not self.member.isAttr():
             raise TypeError("Event code generator does not support methods")
         if self.member.isStatic():
             raise TypeError("Event code generators does not support static attributes")
         return CGNativeMember.getArgs(self, returnType, argList)
 
     def getMethodBody(self):
         type = self.member.type
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -563,45 +563,62 @@ class Descriptor(DescriptorProvider):
     @property
     def hasNamedPropertiesObject(self):
         if self.interface.isExternal():
             return False
 
         return self.isGlobal() and self.supportsNamedProperties()
 
     def getExtendedAttributes(self, member, getter=False, setter=False):
-        def ensureValidThrowsExtendedAttribute(attr):
+        def ensureValidBoolExtendedAttribute(attr, name):
             if (attr is not None and attr is not True):
-                raise TypeError("Unknown value for 'Throws': " + attr[0])
+                raise TypeError("Unknown value for '%s': %s" % (name, attr[0]))
+
+        def ensureValidThrowsExtendedAttribute(attr):
+            ensureValidBoolExtendedAttribute(attr, "Throws")
+
+        def ensureValidCanOOMExtendedAttribute(attr):
+            ensureValidBoolExtendedAttribute(attr, "CanOOM")
 
         def maybeAppendInfallibleToAttrs(attrs, throws):
             ensureValidThrowsExtendedAttribute(throws)
             if throws is None:
                 attrs.append("infallible")
 
+        def maybeAppendCanOOMToAttrs(attrs, canOOM):
+            ensureValidCanOOMExtendedAttribute(canOOM)
+            if canOOM is not None:
+                attrs.append("canOOM")
+
         name = member.identifier.name
         throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
+        canOOM = member.getExtendedAttribute("CanOOM")
         if member.isMethod():
             # JSObject-returning [NewObject] methods must be fallible,
             # since they have to (fallibly) allocate the new JSObject.
             if (member.getExtendedAttribute("NewObject") and
                 methodReturnsJSObject(member)):
                 throws = True
             attrs = self.extendedAttributes['all'].get(name, [])
             maybeAppendInfallibleToAttrs(attrs, throws)
+            maybeAppendCanOOMToAttrs(attrs, canOOM)
             return attrs
 
         assert member.isAttr()
         assert bool(getter) != bool(setter)
         key = 'getterOnly' if getter else 'setterOnly'
         attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
         if throws is None:
             throwsAttr = "GetterThrows" if getter else "SetterThrows"
             throws = member.getExtendedAttribute(throwsAttr)
         maybeAppendInfallibleToAttrs(attrs, throws)
+        if canOOM is None:
+            canOOMAttr = "GetterCanOOM" if getter else "SetterCanOOM"
+            canOOM = member.getExtendedAttribute(canOOMAttr)
+        maybeAppendCanOOMToAttrs(attrs, canOOM)
         return attrs
 
     def supportsIndexedProperties(self):
         return self.operations['IndexedGetter'] is not None
 
     def lengthNeedsCallerType(self):
         """
         Determine whether our length getter needs a caller type; this is needed
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -97,16 +97,17 @@ struct StringArrayAppender
     aArgs.AppendElement(aFirst);
     Append(aArgs, aCount - 1, Forward<Ts>(aOtherArgs)...);
   }
 };
 
 } // namespace dom
 
 class ErrorResult;
+class OOMReporter;
 
 namespace binding_danger {
 
 /**
  * Templated implementation class for various ErrorResult-like things.  The
  * instantiations differ only in terms of their cleanup policies (used in the
  * destructor), which they can specify via the template argument.  Note that
  * this means it's safe to reinterpret_cast between the instantiations unless
@@ -157,16 +158,17 @@ public:
 
   explicit TErrorResult(nsresult aRv)
     : TErrorResult()
   {
     AssignErrorCode(aRv);
   }
 
   operator ErrorResult&();
+  operator OOMReporter&();
 
   void Throw(nsresult rv) {
     MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
     AssignErrorCode(rv);
   }
 
   // Duplicate our current state on the given TErrorResult object.  Any
   // existing errors or messages on the target will be suppressed before
@@ -532,16 +534,92 @@ binding_danger::TErrorResult<CleanupPoli
 // A class for use when an ErrorResult should just automatically be ignored.
 // This doesn't inherit from ErrorResult so we don't make two separate calls to
 // SuppressException.
 class IgnoredErrorResult :
     public binding_danger::TErrorResult<binding_danger::JustSuppressCleanupPolicy>
 {
 };
 
+namespace dom {
+namespace binding_detail {
+class FastErrorResult :
+    public mozilla::binding_danger::TErrorResult<
+      mozilla::binding_danger::JustAssertCleanupPolicy>
+{
+};
+} // namespace binding_detail
+} // namespace dom
+
+// This part is a bit annoying.  We want an OOMReporter class that has the
+// following properties:
+//
+// 1) Can be cast to from any ErrorResult-like type.
+// 2) Has a fast destructor (because we want to use it from bindings).
+// 3) Won't be randomly instantiated by non-binding code (because the fast
+//    destructor is not so safe.
+// 4) Doesn't look ugly on the callee side (e.g. isn't in the binding_detail or
+//    binding_danger namespace).
+//
+// We do this by having two classes: The class callees should use, which has the
+// things we want and a private constructor, and a friend subclass in the
+// binding_danger namespace that can be used to construct it.
+namespace binding_danger {
+class OOMReporterInstantiator;
+} // namespace binding_danger
+
+class OOMReporter : private dom::binding_detail::FastErrorResult
+{
+public:
+  void ReportOOM()
+  {
+    Throw(NS_ERROR_OUT_OF_MEMORY);
+  }
+
+private:
+  // OOMReporterInstantiator is a friend so it can call our constructor and
+  // MaybeSetPendingException.
+  friend class binding_danger::OOMReporterInstantiator;
+
+  // TErrorResult is a friend so its |operator OOMReporter&()| can work.
+  template<typename CleanupPolicy>
+  friend class binding_danger::TErrorResult;
+
+  OOMReporter()
+    : dom::binding_detail::FastErrorResult()
+  {
+  }
+};
+
+namespace binding_danger {
+class OOMReporterInstantiator : public OOMReporter
+{
+public:
+  OOMReporterInstantiator()
+    : OOMReporter()
+  {
+  }
+
+  // We want to be able to call MaybeSetPendingException from codegen.  The one
+  // on OOMReporter is not callable directly, because it comes from a private
+  // superclass.  But we're a friend, so _we_ can call it.
+  bool MaybeSetPendingException(JSContext* cx)
+  {
+    return OOMReporter::MaybeSetPendingException(cx);
+  }
+};
+} // namespace binding_danger
+
+template<typename CleanupPolicy>
+binding_danger::TErrorResult<CleanupPolicy>::operator OOMReporter&()
+{
+  return *static_cast<OOMReporter*>(
+     reinterpret_cast<TErrorResult<JustAssertCleanupPolicy>*>(this));
+}
+
 /******************************************************************************
  ** Macros for checking results
  ******************************************************************************/
 
 #define ENSURE_SUCCESS(res, ret)                                          \
   do {                                                                    \
     if (res.Failed()) {                                                   \
       nsCString msg;                                                      \
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -4078,25 +4078,29 @@ class IDLAttribute(IDLInterfaceMember):
                                   [self.location])
         if not self.type.unroll().isExposedInAllOf(self.exposureSet):
             raise WebIDLError("Attribute returns a type that is not exposed "
                               "everywhere where the attribute is exposed",
                               [self.location])
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
-        if identifier == "SetterThrows" and self.readonly:
+        if ((identifier == "SetterThrows" or identifier == "SetterCanOOM")
+            and self.readonly):
             raise WebIDLError("Readonly attributes must not be flagged as "
-                              "[SetterThrows]",
+                              "[%s]" % identifier,
                               [self.location])
-        elif (((identifier == "Throws" or identifier == "GetterThrows") and
+        elif (((identifier == "Throws" or identifier == "GetterThrows" or
+                identifier == "CanOOM" or identifier == "GetterCanOOM") and
                self.getExtendedAttribute("StoreInSlot")) or
               (identifier == "StoreInSlot" and
                (self.getExtendedAttribute("Throws") or
-                self.getExtendedAttribute("GetterThrows")))):
+                self.getExtendedAttribute("GetterThrows") or
+                self.getExtendedAttribute("CanOOM") or
+                self.getExtendedAttribute("GetterCanOOM")))):
             raise WebIDLError("Throwing things can't be [StoreInSlot]",
                               [attr.location])
         elif identifier == "LenientThis":
             if not attr.noArguments():
                 raise WebIDLError("[LenientThis] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[LenientThis] is only allowed on non-static "
@@ -4250,16 +4254,19 @@ class IDLAttribute(IDLInterfaceMember):
                 raise WebIDLError("[Unscopable] is only allowed on non-static "
                                   "attributes and operations",
                                   [attr.location, self.location])
         elif (identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "SetterThrows" or
               identifier == "Throws" or
               identifier == "GetterThrows" or
+              identifier == "SetterCanOOM" or
+              identifier == "CanOOM" or
+              identifier == "GetterCanOOM" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
               identifier == "NeedsSubjectPrincipal" or
               identifier == "NeedsCallerType" or
@@ -4897,23 +4904,22 @@ class IDLMethod(IDLInterfaceMember, IDLS
         # No valid distinguishing index.  Time to throw
         locations = self.locationsForArgCount(argc)
         raise WebIDLError("Signatures with %d arguments for method '%s' are not "
                           "distinguishable" % (argc, self.identifier.name),
                           locations)
 
     def handleExtendedAttribute(self, attr):
         identifier = attr.identifier()
-        if identifier == "GetterThrows":
+        if (identifier == "GetterThrows" or
+            identifier == "SetterThrows" or
+            identifier == "GetterCanOOM" or
+            identifier == "SetterCanOOM"):
             raise WebIDLError("Methods must not be flagged as "
-                              "[GetterThrows]",
-                              [attr.location, self.location])
-        elif identifier == "SetterThrows":
-            raise WebIDLError("Methods must not be flagged as "
-                              "[SetterThrows]",
+                              "[%s]" % identifier,
                               [attr.location, self.location])
         elif identifier == "Unforgeable":
             if self.isStatic():
                 raise WebIDLError("[Unforgeable] is only allowed on non-static "
                                   "methods", [attr.location, self.location])
             self._unforgeable = True
         elif identifier == "SameObject":
             raise WebIDLError("Methods must not be flagged as [SameObject]",
@@ -4976,16 +4982,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
             if not attr.noArguments():
                 raise WebIDLError("[Unscopable] must take no arguments",
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[Unscopable] is only allowed on non-static "
                                   "attributes and operations",
                                   [attr.location, self.location])
         elif (identifier == "Throws" or
+              identifier == "CanOOM" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "BinaryName" or
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -931,16 +931,23 @@ public:
   void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
+  void CanOOMMethod(OOMReporter& aRv);
+  bool GetCanOOMAttr(OOMReporter& aRv) const;
+  void SetCanOOMAttr(bool arg, OOMReporter& aRv);
+  bool GetCanOOMGetterAttr(OOMReporter& aRv) const;
+  void SetCanOOMGetterAttr(bool arg);
+  bool CanOOMSetterAttr() const;
+  void SetCanOOMSetterAttr(bool arg, OOMReporter& aRv);
   void NeedsSubjectPrincipalMethod(nsIPrincipal&);
   bool NeedsSubjectPrincipalAttr(nsIPrincipal&);
   void SetNeedsSubjectPrincipalAttr(bool, nsIPrincipal&);
   void NeedsCallerTypeMethod(CallerType);
   bool NeedsCallerTypeAttr(CallerType);
   void SetNeedsCallerTypeAttr(bool, CallerType);
   int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -941,16 +941,20 @@ interface TestInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [CanOOM] void canOOMMethod();
+  [CanOOM] attribute boolean canOOMAttr;
+  [GetterCanOOM] attribute boolean canOOMGetterAttr;
+  [SetterCanOOM] attribute boolean canOOMSetterAttr;
   [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   [NeedsCallerType] void needsCallerTypeMethod();
   [NeedsCallerType] attribute boolean needsCallerTypeAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -771,16 +771,20 @@ interface TestExampleInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestExampleInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [CanOOM] void canOOMMethod();
+  [CanOOM] attribute boolean canOOMAttr;
+  [GetterCanOOM] attribute boolean canOOMGetterAttr;
+  [SetterCanOOM] attribute boolean canOOMSetterAttr;
   [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   [NeedsCallerType] void needsCallerTypeMethod();
   [NeedsCallerType] attribute boolean needsCallerTypeAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -792,16 +792,20 @@ interface TestJSImplInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestJSImplInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestJSImplInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestJSImplInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [CanOOM] void canOOMMethod();
+  [CanOOM] attribute boolean canOOMAttr;
+  [GetterCanOOM] attribute boolean canOOMGetterAttr;
+  [SetterCanOOM] attribute boolean canOOMSetterAttr;
   // NeedsSubjectPrincipal not supported on JS-implemented things for
   // now, because we always pass in the caller principal anyway.
   //  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   //  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   // legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -109,16 +109,19 @@ WebGLContextOptions::WebGLContextOptions
 }
 
 
 /*static*/ const uint32_t WebGLContext::kMinMaxColorAttachments = 4;
 /*static*/ const uint32_t WebGLContext::kMinMaxDrawBuffers = 4;
 
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
+    , mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings())
+    , mNumPerfWarnings(0)
+    , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
     , mBufferFetchingIsVerified(false)
     , mBufferFetchingHasPerVertex(false)
     , mMaxFetchedVertices(0)
     , mMaxFetchedInstances(0)
     , mLayerIsMirror(false)
     , mBypassShaderValidation(false)
     , mEmptyTFO(0)
     , mContextLossHandler(this)
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -326,16 +326,20 @@ class WebGLContext
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
         UNMASKED_RENDERER_WEBGL = 0x9246
     };
 
     static const uint32_t kMinMaxColorAttachments;
     static const uint32_t kMinMaxDrawBuffers;
 
+    const uint32_t mMaxPerfWarnings;
+    mutable uint64_t mNumPerfWarnings;
+    const uint32_t mMaxAcceptableFBStatusInvals;
+
 public:
     WebGLContext();
 
 protected:
     virtual ~WebGLContext();
 
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -1932,16 +1936,20 @@ protected:
     int mDrawCallsSinceLastFlush;
 
     int mAlreadyGeneratedWarnings;
     int mMaxWarnings;
     bool mAlreadyWarnedAboutFakeVertexAttrib0;
 
     bool ShouldGenerateWarnings() const;
 
+    bool ShouldGeneratePerfWarnings() const {
+        return mNumPerfWarnings < mMaxPerfWarnings;
+    }
+
     uint64_t mLastUseIndex;
 
     bool mNeedsFakeNoAlpha;
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
     bool mNeedsEmulatedLoneDepthStencil;
 
     const bool mAllowFBInvalidation;
@@ -2024,16 +2032,18 @@ protected:
     ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
 #endif
 
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...);
     void GenerateWarning(const char* fmt, va_list ap);
 
+    void GeneratePerfWarning(const char* fmt, ...) const;
+
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
 
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
 
 
     const decltype(mBound2DTextures)* TexListForElemType(GLenum elemType) const;
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -343,44 +343,46 @@ WebGLContext::DeleteFramebuffer(WebGLFra
         BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
                         static_cast<WebGLFramebuffer*>(nullptr));
     }
 }
 
 void
 WebGLContext::DeleteRenderbuffer(WebGLRenderbuffer* rbuf)
 {
-    if (!ValidateDeleteObject("deleteRenderbuffer", rbuf))
+    const char funcName[] = "deleteRenderbuffer";
+    if (!ValidateDeleteObject(funcName, rbuf))
         return;
 
     if (mBoundDrawFramebuffer)
-        mBoundDrawFramebuffer->DetachRenderbuffer(rbuf);
+        mBoundDrawFramebuffer->DetachRenderbuffer(funcName, rbuf);
 
     if (mBoundReadFramebuffer)
-        mBoundReadFramebuffer->DetachRenderbuffer(rbuf);
-
-    rbuf->InvalidateStatusOfAttachedFBs();
+        mBoundReadFramebuffer->DetachRenderbuffer(funcName, rbuf);
+
+    rbuf->InvalidateStatusOfAttachedFBs(funcName);
 
     if (mBoundRenderbuffer == rbuf)
         BindRenderbuffer(LOCAL_GL_RENDERBUFFER, nullptr);
 
     rbuf->RequestDelete();
 }
 
 void
 WebGLContext::DeleteTexture(WebGLTexture* tex)
 {
-    if (!ValidateDeleteObject("deleteTexture", tex))
+    const char funcName[] = "deleteTexture";
+    if (!ValidateDeleteObject(funcName, tex))
         return;
 
     if (mBoundDrawFramebuffer)
-        mBoundDrawFramebuffer->DetachTexture(tex);
+        mBoundDrawFramebuffer->DetachTexture(funcName, tex);
 
     if (mBoundReadFramebuffer)
-        mBoundReadFramebuffer->DetachTexture(tex);
+        mBoundReadFramebuffer->DetachTexture(funcName, tex);
 
     GLuint activeTexture = mActiveTexture;
     for (int32_t i = 0; i < mGLMaxTextureUnits; i++) {
         if (mBound2DTextures[i] == tex ||
             mBoundCubeMapTextures[i] == tex ||
             mBound3DTextures[i] == tex ||
             mBound2DArrayTextures[i] == tex)
         {
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -85,17 +85,17 @@ WebGLContext::GenerateWarning(const char
     }
 
     dom::AutoJSAPI api;
     if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) {
         return;
     }
 
     JSContext* cx = api.cx();
-    JS_ReportWarningASCII(cx, "WebGL: %s", buf);
+    JS_ReportWarningASCII(cx, "WebGL warning: %s", buf);
     if (!ShouldGenerateWarnings()) {
         JS_ReportWarningASCII(cx,
                               "WebGL: No further warnings will be reported for"
                               " this WebGL context."
                               " (already reported %d warnings)",
                               mAlreadyGeneratedWarnings);
     }
 }
@@ -105,16 +105,53 @@ WebGLContext::ShouldGenerateWarnings() c
 {
     if (mMaxWarnings == -1)
         return true;
 
     return mAlreadyGeneratedWarnings < mMaxWarnings;
 }
 
 void
+WebGLContext::GeneratePerfWarning(const char* fmt, ...) const
+{
+    if (!ShouldGeneratePerfWarnings())
+        return;
+
+    if (!mCanvasElement)
+        return;
+
+    dom::AutoJSAPI api;
+    if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject()))
+        return;
+    JSContext* cx = api.cx();
+
+    ////
+
+    va_list ap;
+    va_start(ap, fmt);
+
+    char buf[1024];
+    PR_vsnprintf(buf, 1024, fmt, ap);
+
+    va_end(ap);
+
+    ////
+
+    JS_ReportWarningASCII(cx, "WebGL perf warning: %s", buf);
+    mNumPerfWarnings++;
+
+    if (!ShouldGeneratePerfWarnings()) {
+        JS_ReportWarningASCII(cx,
+                              "WebGL: After reporting %u, no further perf warnings will"
+                              " be reported for this WebGL context.",
+                              uint32_t(mNumPerfWarnings));
+    }
+}
+
+void
 WebGLContext::SynthesizeGLError(GLenum err)
 {
     /* ES2 section 2.5 "GL Errors" states that implementations can have
      * multiple 'flags', as errors might be caught in different parts of
      * a distributed implementation.
      * We're signing up as a distributed implementation here, with
      * separate flags for WebGL and the underlying GLContext.
      */
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -42,17 +42,18 @@ WebGLFBAttachPoint::~WebGLFBAttachPoint(
     MOZ_ASSERT(mFB, "Should have been Init'd.");
     MOZ_ASSERT(!mRenderbufferPtr);
     MOZ_ASSERT(!mTexturePtr);
 }
 
 void
 WebGLFBAttachPoint::Unlink()
 {
-    Clear();
+    const char funcName[] = "WebGLFramebuffer::GC";
+    Clear(funcName);
 }
 
 bool
 WebGLFBAttachPoint::IsDeleteRequested() const
 {
     return Texture() ? Texture()->IsDeleteRequested()
          : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
          : false;
@@ -109,51 +110,51 @@ WebGLFBAttachPoint::IsReadableFloat() co
     auto format = formatUsage->format;
     if (!format->IsColorFormat())
         return false;
 
     return format->componentType == webgl::ComponentType::Float;
 }
 
 void
-WebGLFBAttachPoint::Clear()
+WebGLFBAttachPoint::Clear(const char* funcName)
 {
     if (mRenderbufferPtr) {
         MOZ_ASSERT(!mTexturePtr);
         mRenderbufferPtr->UnmarkAttachment(*this);
     } else if (mTexturePtr) {
         mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).RemoveAttachPoint(this);
     }
 
     mTexturePtr = nullptr;
     mRenderbufferPtr = nullptr;
 
-    OnBackingStoreRespecified();
+    OnBackingStoreRespecified(funcName);
 }
 
 void
-WebGLFBAttachPoint::SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level,
-                                GLint layer)
+WebGLFBAttachPoint::SetTexImage(const char* funcName, WebGLTexture* tex,
+                                TexImageTarget target, GLint level, GLint layer)
 {
-    Clear();
+    Clear(funcName);
 
     mTexturePtr = tex;
     mTexImageTarget = target;
     mTexImageLevel = level;
     mTexImageLayer = layer;
 
     if (mTexturePtr) {
         mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).AddAttachPoint(this);
     }
 }
 
 void
-WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb)
+WebGLFBAttachPoint::SetRenderbuffer(const char* funcName, WebGLRenderbuffer* rb)
 {
-    Clear();
+    Clear(funcName);
 
     mRenderbufferPtr = rb;
 
     if (mRenderbufferPtr) {
         mRenderbufferPtr->MarkAttachment(*this);
     }
 }
 
@@ -221,19 +222,19 @@ WebGLFBAttachPoint::Size(uint32_t* const
     MOZ_ASSERT(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined());
     const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel);
 
     *out_width = imageInfo.mWidth;
     *out_height = imageInfo.mHeight;
 }
 
 void
-WebGLFBAttachPoint::OnBackingStoreRespecified() const
+WebGLFBAttachPoint::OnBackingStoreRespecified(const char* funcName) const
 {
-    mFB->InvalidateFramebufferStatus();
+    mFB->InvalidateFramebufferStatus(funcName);
 }
 
 void
 WebGLFBAttachPoint::AttachmentName(nsCString* out) const
 {
     switch (mAttachmentPoint) {
     case LOCAL_GL_DEPTH_ATTACHMENT:
         out->AssignLiteral("DEPTH_ATTACHMENT");
@@ -614,16 +615,17 @@ WebGLFBAttachPoint::GetParameter(const c
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 // WebGLFramebuffer
 
 WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo)
     : WebGLRefCountedObject(webgl)
     , mGLName(fbo)
+    , mNumFBStatusInvals(0)
 #ifdef ANDROID
     , mIsFB(false)
 #endif
     , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT)
     , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT)
     , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
 {
     mContext->mFramebuffers.insertBack(this);
@@ -636,24 +638,26 @@ WebGLFramebuffer::WebGLFramebuffer(WebGL
 
     mColorDrawBuffers.push_back(&mColorAttachments[0]);
     mColorReadBuffer = &mColorAttachments[0];
 }
 
 void
 WebGLFramebuffer::Delete()
 {
-    InvalidateFramebufferStatus();
+    const char funcName[] = "WebGLFramebuffer::Delete";
+
+    InvalidateFramebufferStatus(funcName);
 
-    mDepthAttachment.Clear();
-    mStencilAttachment.Clear();
-    mDepthStencilAttachment.Clear();
+    mDepthAttachment.Clear(funcName);
+    mStencilAttachment.Clear(funcName);
+    mDepthStencilAttachment.Clear(funcName);
 
     for (auto& cur : mColorAttachments) {
-        cur.Clear();
+        cur.Clear(funcName);
     }
 
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteFramebuffers(1, &mGLName);
 
     LinkedListElement<WebGLFramebuffer>::removeFrom(mContext->mFramebuffers);
 
 #ifdef ANDROID
@@ -704,33 +708,33 @@ WebGLFramebuffer::GetAttachPoint(GLenum 
     X(mStencilAttachment);                \
     X(mDepthStencilAttachment);           \
                                           \
     for (auto& cur : mColorAttachments) { \
         X(cur);                           \
     }
 
 void
-WebGLFramebuffer::DetachTexture(const WebGLTexture* tex)
+WebGLFramebuffer::DetachTexture(const char* funcName, const WebGLTexture* tex)
 {
     const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
         if (attach.Texture() == tex) {
-            attach.Clear();
+            attach.Clear(funcName);
         }
     };
 
     FOR_EACH_ATTACHMENT(fnDetach)
 }
 
 void
-WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb)
+WebGLFramebuffer::DetachRenderbuffer(const char* funcName, const WebGLRenderbuffer* rb)
 {
     const auto fnDetach = [&](WebGLFBAttachPoint& attach) {
         if (attach.Renderbuffer() == rb) {
-            attach.Clear();
+            attach.Clear(funcName);
         }
     };
 
     FOR_EACH_ATTACHMENT(fnDetach)
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Completeness
@@ -1131,16 +1135,31 @@ WebGLFramebuffer::ResolvedData::Resolved
         if (!fnCommon(attach))
             return;
 
         readSet.insert(WebGLFBAttachPoint::Ordered(attach));
     }
 }
 
 void
+WebGLFramebuffer::InvalidateFramebufferStatus(const char* funcName)
+{
+    if (mResolvedCompleteData) {
+        mNumFBStatusInvals++;
+        if (mNumFBStatusInvals > mContext->mMaxAcceptableFBStatusInvals) {
+            mContext->GeneratePerfWarning("%s: FB was invalidated after being complete %u"
+                                          " times.",
+                                          funcName, uint32_t(mNumFBStatusInvals));
+        }
+    }
+
+    mResolvedCompleteData = nullptr;
+}
+
+void
 WebGLFramebuffer::RefreshResolvedData()
 {
     if (mResolvedCompleteData) {
         mResolvedCompleteData.reset(new ResolvedData(*this));
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1349,23 +1368,23 @@ WebGLFramebuffer::FramebufferRenderbuffe
 
     // `rb`
     if (rb && !mContext->ValidateObject("framebufferRenderbuffer: rb", *rb))
         return;
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-        mDepthAttachment.SetRenderbuffer(rb);
-        mStencilAttachment.SetRenderbuffer(rb);
+        mDepthAttachment.SetRenderbuffer(funcName, rb);
+        mStencilAttachment.SetRenderbuffer(funcName, rb);
     } else {
-        attach->SetRenderbuffer(rb);
+        attach->SetRenderbuffer(funcName, rb);
     }
 
-    InvalidateFramebufferStatus();
+    InvalidateFramebufferStatus(funcName);
 }
 
 void
 WebGLFramebuffer::FramebufferTexture2D(const char* funcName, GLenum attachEnum,
                                        GLenum texImageTarget, WebGLTexture* tex,
                                        GLint level)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
@@ -1437,23 +1456,23 @@ WebGLFramebuffer::FramebufferTexture2D(c
         }
     } else if (level != 0) {
         return mContext->ErrorInvalidValue("%s: `level` must be 0.", funcName);
     }
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-        mDepthAttachment.SetTexImage(tex, texImageTarget, level);
-        mStencilAttachment.SetTexImage(tex, texImageTarget, level);
+        mDepthAttachment.SetTexImage(funcName, tex, texImageTarget, level);
+        mStencilAttachment.SetTexImage(funcName, tex, texImageTarget, level);
     } else {
-        attach->SetTexImage(tex, texImageTarget, level);
+        attach->SetTexImage(funcName, tex, texImageTarget, level);
     }
 
-    InvalidateFramebufferStatus();
+    InvalidateFramebufferStatus(funcName);
 }
 
 void
 WebGLFramebuffer::FramebufferTextureLayer(const char* funcName, GLenum attachEnum,
                                           WebGLTexture* tex, GLint level, GLint layer)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
@@ -1521,23 +1540,23 @@ WebGLFramebuffer::FramebufferTextureLaye
                                             funcName);
             return;
         }
     }
 
     // End of validation.
 
     if (mContext->IsWebGL2() && attachEnum == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
-        mDepthAttachment.SetTexImage(tex, texImageTarget, level, layer);
-        mStencilAttachment.SetTexImage(tex, texImageTarget, level, layer);
+        mDepthAttachment.SetTexImage(funcName, tex, texImageTarget, level, layer);
+        mStencilAttachment.SetTexImage(funcName, tex, texImageTarget, level, layer);
     } else {
-        attach->SetTexImage(tex, texImageTarget, level, layer);
+        attach->SetTexImage(funcName, tex, texImageTarget, level, layer);
     }
 
-    InvalidateFramebufferStatus();
+    InvalidateFramebufferStatus(funcName);
 }
 
 JS::Value
 WebGLFramebuffer::GetAttachmentParameter(const char* funcName, JSContext* cx,
                                          GLenum target, GLenum attachEnum, GLenum pname,
                                          ErrorResult* const out_error)
 {
     const auto maybeAttach = GetAttachPoint(attachEnum);
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -61,21 +61,21 @@ public:
     bool IsDeleteRequested() const;
 
     const webgl::FormatUsageInfo* Format() const;
     uint32_t Samples() const;
 
     bool HasAlpha() const;
     bool IsReadableFloat() const;
 
-    void Clear();
+    void Clear(const char* funcName);
 
-    void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level,
-                     GLint layer = 0);
-    void SetRenderbuffer(WebGLRenderbuffer* rb);
+    void SetTexImage(const char* funcName, WebGLTexture* tex, TexImageTarget target,
+                     GLint level, GLint layer = 0);
+    void SetRenderbuffer(const char* funcName, WebGLRenderbuffer* rb);
 
     WebGLTexture* Texture() const { return mTexturePtr; }
     WebGLRenderbuffer* Renderbuffer() const { return mRenderbufferPtr; }
 
     TexImageTarget ImageTarget() const {
         return mTexImageTarget;
     }
     GLint Layer() const {
@@ -95,17 +95,17 @@ public:
     bool IsComplete(WebGLContext* webgl, nsCString* const out_info) const;
 
     void Resolve(gl::GLContext* gl) const;
 
     JS::Value GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx,
                            GLenum target, GLenum attachment, GLenum pname,
                            ErrorResult* const out_error) const;
 
-    void OnBackingStoreRespecified() const;
+    void OnBackingStoreRespecified(const char* funcName) const;
 
     bool IsEquivalentForFeedback(const WebGLFBAttachPoint& other) const {
         if (!IsDefined() || !other.IsDefined())
             return false;
 
 #define _(X) X == other.X
         return ( _(mRenderbufferPtr) &&
                  _(mTexturePtr) &&
@@ -149,16 +149,19 @@ class WebGLFramebuffer final
 {
     friend class WebGLContext;
 
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     const GLuint mGLName;
 
+private:
+    uint64_t mNumFBStatusInvals;
+
 protected:
 #ifdef ANDROID
     // Bug 1140459: Some drivers (including our test slaves!) don't
     // give reasonable answers for IsRenderbuffer, maybe others.
     // This shows up on Android 2.3 emulator.
     //
     // So we track the `is a Framebuffer` state ourselves.
     bool mIsFB;
@@ -225,18 +228,18 @@ protected:
     Maybe<WebGLFBAttachPoint*> GetAttachPoint(GLenum attachment); // Fallible
     Maybe<WebGLFBAttachPoint*> GetColorAttachPoint(GLenum attachment); // Fallible
     void ResolveAttachments() const;
     void RefreshDrawBuffers() const;
     void RefreshReadBuffer() const;
     bool ResolveAttachmentData(const char* funcName) const;
 
 public:
-    void DetachTexture(const WebGLTexture* tex);
-    void DetachRenderbuffer(const WebGLRenderbuffer* rb);
+    void DetachTexture(const char* funcName, const WebGLTexture* tex);
+    void DetachRenderbuffer(const char* funcName, const WebGLRenderbuffer* rb);
     bool ValidateAndInitAttachments(const char* funcName);
     bool ValidateClearBufferType(const char* funcName, GLenum buffer, uint32_t drawBuffer,
                                  GLenum funcType) const;
 
     bool ValidateForRead(const char* info,
                          const webgl::FormatUsageInfo** const out_format,
                          uint32_t* const out_width, uint32_t* const out_height);
 
@@ -253,21 +256,17 @@ public:
     GETTER(ResolvedCompleteData)
 
 #undef GETTER
 
     ////////////////
     // Invalidation
 
     bool IsResolvedComplete() const { return bool(mResolvedCompleteData); }
-
-    void InvalidateFramebufferStatus() {
-        mResolvedCompleteData = nullptr;
-    }
-
+    void InvalidateFramebufferStatus(const char* funcName);
     void RefreshResolvedData();
 
     ////////////////
     // WebGL funcs
 
     FBStatus CheckFramebufferStatus(const char* funcName);
     void FramebufferRenderbuffer(const char* funcName, GLenum attachment, GLenum rbtarget,
                                  WebGLRenderbuffer* rb);
--- a/dom/canvas/WebGLFramebufferAttachable.cpp
+++ b/dom/canvas/WebGLFramebufferAttachable.cpp
@@ -26,18 +26,18 @@ WebGLFramebufferAttachable::UnmarkAttach
         MOZ_ASSERT(false, "Is not attached to FB");
         return;
     }
 
     mAttachmentPoints.RemoveElementAt(i);
 }
 
 void
-WebGLFramebufferAttachable::InvalidateStatusOfAttachedFBs() const
+WebGLFramebufferAttachable::InvalidateStatusOfAttachedFBs(const char* funcName) const
 {
     const size_t count = mAttachmentPoints.Length();
     for (size_t i = 0; i < count; ++i) {
         MOZ_ASSERT(mAttachmentPoints[i]->mFB);
-        mAttachmentPoints[i]->mFB->InvalidateFramebufferStatus();
+        mAttachmentPoints[i]->mFB->InvalidateFramebufferStatus(funcName);
     }
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLFramebufferAttachable.h
+++ b/dom/canvas/WebGLFramebufferAttachable.h
@@ -14,14 +14,14 @@ class WebGLFBAttachPoint;
 class WebGLFramebufferAttachable
 {
     nsTArray<const WebGLFBAttachPoint*> mAttachmentPoints;
 
 public:
     // Track FBO/Attachment combinations
     void MarkAttachment(const WebGLFBAttachPoint& attachment);
     void UnmarkAttachment(const WebGLFBAttachPoint& attachment);
-    void InvalidateStatusOfAttachedFBs() const;
+    void InvalidateStatusOfAttachedFBs(const char* funcName) const;
 };
 
 } // namespace mozilla
 
 #endif // !WEBGLFRAMEBUFFERATTACHABLE_H_
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -219,17 +219,17 @@ WebGLRenderbuffer::RenderbufferStorage(c
     }
 
     mSamples = samples;
     mFormat = usage;
     mWidth = width;
     mHeight = height;
     mImageDataStatus = WebGLImageDataStatus::UninitializedImageData;
 
-    InvalidateStatusOfAttachedFBs();
+    InvalidateStatusOfAttachedFBs(funcName);
 }
 
 void
 WebGLRenderbuffer::DoFramebufferRenderbuffer(FBTarget target, GLenum attachment) const
 {
     gl::GLContext* gl = mContext->gl;
 
     if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -28,48 +28,46 @@ namespace mozilla {
 template <typename T>
 static inline T&
 Mutable(const T& x)
 {
     return const_cast<T&>(x);
 }
 
 void
-WebGLTexture::ImageInfo::Clear()
+WebGLTexture::ImageInfo::Clear(const char* funcName)
 {
     if (!IsDefined())
         return;
 
-    OnRespecify();
+    OnRespecify(funcName);
 
     Mutable(mFormat) = LOCAL_GL_NONE;
     Mutable(mWidth) = 0;
     Mutable(mHeight) = 0;
     Mutable(mDepth) = 0;
 
     MOZ_ASSERT(!IsDefined());
 }
 
-WebGLTexture::ImageInfo&
-WebGLTexture::ImageInfo::operator =(const ImageInfo& a)
+void
+WebGLTexture::ImageInfo::Set(const char* funcName, const ImageInfo& a)
 {
     MOZ_ASSERT(a.IsDefined());
 
     Mutable(mFormat) = a.mFormat;
     Mutable(mWidth) = a.mWidth;
     Mutable(mHeight) = a.mHeight;
     Mutable(mDepth) = a.mDepth;
 
     mIsDataInitialized = a.mIsDataInitialized;
 
     // But *don't* transfer mAttachPoints!
     MOZ_ASSERT(a.mAttachPoints.empty());
-    OnRespecify();
-
-    return *this;
+    OnRespecify(funcName);
 }
 
 bool
 WebGLTexture::ImageInfo::IsPowerOfTwo() const
 {
     return mozilla::IsPowerOfTwo(mWidth) &&
            mozilla::IsPowerOfTwo(mHeight) &&
            mozilla::IsPowerOfTwo(mDepth);
@@ -86,20 +84,20 @@ WebGLTexture::ImageInfo::AddAttachPoint(
 void
 WebGLTexture::ImageInfo::RemoveAttachPoint(WebGLFBAttachPoint* attachPoint)
 {
     DebugOnly<size_t> numElemsErased = mAttachPoints.erase(attachPoint);
     MOZ_ASSERT_IF(IsDefined(), numElemsErased == 1);
 }
 
 void
-WebGLTexture::ImageInfo::OnRespecify() const
+WebGLTexture::ImageInfo::OnRespecify(const char* funcName) const
 {
     for (auto cur : mAttachPoints) {
-        cur->OnBackingStoreRespecified();
+        cur->OnBackingStoreRespecified(funcName);
     }
 }
 
 size_t
 WebGLTexture::ImageInfo::MemoryUsage() const
 {
     if (!IsDefined())
         return 0;
@@ -144,18 +142,19 @@ WebGLTexture::WebGLTexture(WebGLContext*
     , mResolved_Swizzle(nullptr)
 {
     mContext->mTextures.insertBack(this);
 }
 
 void
 WebGLTexture::Delete()
 {
+    const char funcName[] = "WebGLTexture::Delete";
     for (auto& cur : mImageInfoArr) {
-        cur.Clear();
+        cur.Clear(funcName);
     }
 
     mContext->MakeContextCurrent();
     mContext->gl->fDeleteTextures(1, &mGLName);
 
     LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
 }
 
@@ -168,28 +167,30 @@ WebGLTexture::MemoryUsage() const
     size_t accum = 0;
     for (const auto& cur : mImageInfoArr) {
         accum += cur.MemoryUsage();
     }
     return accum;
 }
 
 void
-WebGLTexture::SetImageInfo(ImageInfo* target, const ImageInfo& newInfo)
+WebGLTexture::SetImageInfo(const char* funcName, ImageInfo* target,
+                           const ImageInfo& newInfo)
 {
-    *target = newInfo;
+    target->Set(funcName, newInfo);
 
     InvalidateResolveCache();
 }
 
 void
-WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo)
+WebGLTexture::SetImageInfosAtLevel(const char* funcName, uint32_t level,
+                                   const ImageInfo& newInfo)
 {
     for (uint8_t i = 0; i < mFaceCount; i++) {
-        ImageInfoAtFace(i, level) = newInfo;
+        ImageInfoAtFace(i, level).Set(funcName, newInfo);
     }
 
     InvalidateResolveCache();
 }
 
 bool
 WebGLTexture::IsMipmapComplete(const char* funcName, uint32_t texUnit,
                                bool* const out_initFailed)
@@ -768,17 +769,18 @@ WebGLTexture::ClampLevelBaseAndMax()
     //  `[level_base, levels-1]`, where `levels` is the parameter passed to
     //   TexStorage* for the texture object."
     mBaseMipmapLevel = Clamp<uint32_t>(mBaseMipmapLevel, 0, mImmutableLevelCount - 1);
     mMaxMipmapLevel = Clamp<uint32_t>(mMaxMipmapLevel, mBaseMipmapLevel,
                                       mImmutableLevelCount - 1);
 }
 
 void
-WebGLTexture::PopulateMipChain(uint32_t firstLevel, uint32_t lastLevel)
+WebGLTexture::PopulateMipChain(const char* funcName, uint32_t firstLevel,
+                               uint32_t lastLevel)
 {
     const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
     MOZ_ASSERT(baseImageInfo.IsDefined());
 
     uint32_t refWidth = baseImageInfo.mWidth;
     uint32_t refHeight = baseImageInfo.mHeight;
     uint32_t refDepth = baseImageInfo.mDepth;
     if (!refWidth || !refHeight || !refDepth)
@@ -799,17 +801,17 @@ WebGLTexture::PopulateMipChain(uint32_t 
         refHeight = std::max(uint32_t(1), refHeight / 2);
         if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
             refDepth = std::max(uint32_t(1), refDepth / 2);
         }
 
         const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
                             baseImageInfo.IsDataInitialized());
 
-        SetImageInfosAtLevel(level, cur);
+        SetImageInfosAtLevel(funcName, level, cur);
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 // GL calls
 
 bool
 WebGLTexture::BindTexture(TexTarget texTarget)
@@ -849,50 +851,53 @@ WebGLTexture::BindTexture(TexTarget texT
 
     return true;
 }
 
 
 void
 WebGLTexture::GenerateMipmap(TexTarget texTarget)
 {
+    const char funcName[] = "generateMipmap";
     // GLES 3.0.4 p160:
     // "Mipmap generation replaces texel array levels level base + 1 through q with arrays
     //  derived from the level base array, regardless of their previous contents. All
     //  other mipmap arrays, including the level base array, are left unchanged by this
     //  computation."
     const ImageInfo& baseImageInfo = BaseImageInfo();
     if (!baseImageInfo.IsDefined()) {
-        mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture is"
-                                        " not defined.");
+        mContext->ErrorInvalidOperation("%s: The base level of the texture is not"
+                                        " defined.",
+                                        funcName);
         return;
     }
 
     if (IsCubeMap() && !IsCubeComplete()) {
-      mContext->ErrorInvalidOperation("generateMipmap: Cube maps must be \"cube"
-                                      " complete\".");
+      mContext->ErrorInvalidOperation("%s: Cube maps must be \"cube complete\".",
+                                      funcName);
       return;
     }
 
     if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) {
-        mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture"
-                                        " does not have power-of-two dimensions.");
+        mContext->ErrorInvalidOperation("%s: The base level of the texture does not have"
+                                        " power-of-two dimensions.",
+                                        funcName);
         return;
     }
 
     auto format = baseImageInfo.mFormat->format;
     if (format->compression) {
-        mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is"
-                                        " compressed.");
+        mContext->ErrorInvalidOperation("%s: Texture data at base level is compressed.",
+                                        funcName);
         return;
     }
 
     if (format->d) {
-        mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not"
-                                        " supported.");
+        mContext->ErrorInvalidOperation("%s: Depth textures are not supported.",
+                                        funcName);
         return;
     }
 
     // OpenGL ES 3.0.4 p160:
     // If the level base array was not specified with an unsized internal format from
     // table 3.3 or a sized internal format that is both color-renderable and
     // texture-filterable according to table 3.13, an INVALID_OPERATION error
     // is generated.
@@ -905,19 +910,20 @@ WebGLTexture::GenerateMipmap(TexTarget t
         // Non-color-renderable formats from Table 3.3.
         canGenerateMipmap = true;
         break;
     default:
         break;
     }
 
     if (!canGenerateMipmap) {
-        mContext->ErrorInvalidOperation("generateMipmap: Texture at base level is not unsized"
+        mContext->ErrorInvalidOperation("%s: Texture at base level is not unsized"
                                         " internal format or is not"
-                                        " color-renderable or texture-filterable.");
+                                        " color-renderable or texture-filterable.",
+                                        funcName);
         return;
     }
 
     // Done with validation. Do the operation.
 
     mContext->MakeContextCurrent();
     gl::GLContext* gl = mContext->gl;
 
@@ -935,17 +941,17 @@ WebGLTexture::GenerateMipmap(TexTarget t
     } else {
         gl->fGenerateMipmap(texTarget.get());
     }
 
     // Record the results.
     // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
     // mBaseMipmapLevel if the min filter doesn't require mipmaps.
     const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
-    PopulateMipChain(mBaseMipmapLevel, maxLevel);
+    PopulateMipChain(funcName, mBaseMipmapLevel, maxLevel);
 }
 
 JS::Value
 WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
 {
     mContext->MakeContextCurrent();
 
     GLint i = 0;
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -100,27 +100,29 @@ public:
     // numLevels(16k) = log2(16k) + 1 = 14 + 1 = 15
     // numLevels(1M) = log2(1M) + 1 = 19.9 + 1 ~= 21
     // Or we can just max this out to 31, which is the number of unsigned bits in GLsizei.
     static const uint8_t kMaxLevelCount = 31;
 
     // And in turn, it needs these forwards:
 protected:
     // We need to forward these.
-    void SetImageInfo(ImageInfo* target, const ImageInfo& newInfo);
-    void SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo);
+    void SetImageInfo(const char* funcName, ImageInfo* target, const ImageInfo& newInfo);
+    void SetImageInfosAtLevel(const char* funcName, uint32_t level,
+                              const ImageInfo& newInfo);
 
 public:
     // We store information about the various images that are part of this
     // texture. (cubemap faces, mipmap levels)
     class ImageInfo
     {
-        friend void WebGLTexture::SetImageInfo(ImageInfo* target,
+        friend void WebGLTexture::SetImageInfo(const char* funcName, ImageInfo* target,
                                                const ImageInfo& newInfo);
-        friend void WebGLTexture::SetImageInfosAtLevel(uint32_t level,
+        friend void WebGLTexture::SetImageInfosAtLevel(const char* funcName,
+                                                       uint32_t level,
                                                        const ImageInfo& newInfo);
 
     public:
         static const ImageInfo kUndefined;
 
         // This is the "effective internal format" of the texture, an official
         // OpenGL spec concept, see OpenGL ES 3.0.3 spec, section 3.8.3, page
         // 126 and below.
@@ -150,39 +152,38 @@ public:
             , mWidth(width)
             , mHeight(height)
             , mDepth(depth)
             , mIsDataInitialized(isDataInitialized)
         {
             MOZ_ASSERT(mFormat);
         }
 
-        void Clear();
+        void Clear(const char* funcName);
 
         ~ImageInfo() {
-            if (!IsDefined())
-                Clear();
+            MOZ_ASSERT(!mAttachPoints.size());
         }
 
     protected:
-        ImageInfo& operator =(const ImageInfo& a);
+        void Set(const char* funcName, const ImageInfo& a);
 
     public:
         uint32_t PossibleMipmapLevels() const {
             // GLES 3.0.4, 3.8 - Mipmapping: `floor(log2(largest_of_dims)) + 1`
             const uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
             MOZ_ASSERT(largest != 0);
             return FloorLog2Size(largest) + 1;
         }
 
         bool IsPowerOfTwo() const;
 
         void AddAttachPoint(WebGLFBAttachPoint* attachPoint);
         void RemoveAttachPoint(WebGLFBAttachPoint* attachPoint);
-        void OnRespecify() const;
+        void OnRespecify(const char* funcName) const;
 
         size_t MemoryUsage() const;
 
         bool IsDefined() const {
             if (mFormat == LOCAL_GL_NONE) {
                 MOZ_ASSERT(!mWidth && !mHeight && !mDepth);
                 return false;
             }
@@ -284,17 +285,17 @@ public:
                          GLint xOffset, GLint yOffset, GLint zOffset, GLint x, GLint y,
                          GLsizei width, GLsizei height);
 
     ////////////////////////////////////
 
 protected:
     void ClampLevelBaseAndMax();
 
-    void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
+    void PopulateMipChain(const char* funcName, uint32_t baseLevel, uint32_t maxLevel);
 
     bool MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const;
 
     static uint8_t FaceForTarget(TexImageTarget texImageTarget) {
         GLenum rawTexImageTarget = texImageTarget.get();
         switch (rawTexImageTarget) {
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
@@ -325,21 +326,21 @@ public:
         auto face = FaceForTarget(texImageTarget);
         return ImageInfoAtFace(face, level);
     }
 
     const ImageInfo& ImageInfoAt(TexImageTarget texImageTarget, GLint level) const {
         return const_cast<WebGLTexture*>(this)->ImageInfoAt(texImageTarget, level);
     }
 
-    void SetImageInfoAt(TexImageTarget texImageTarget, GLint level,
+    void SetImageInfoAt(const char* funcName, TexImageTarget texImageTarget, GLint level,
                         const ImageInfo& val)
     {
         ImageInfo* target = &ImageInfoAt(texImageTarget, level);
-        SetImageInfo(target, val);
+        SetImageInfo(funcName, target, val);
     }
 
     const ImageInfo& BaseImageInfo() const {
         if (mBaseMipmapLevel >= kMaxLevelCount)
             return ImageInfo::kUndefined;
 
         return ImageInfoAtFace(0, mBaseMipmapLevel);
     }
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1180,19 +1180,19 @@ WebGLTexture::TexStorage(const char* fun
     }
 
     ////////////////////////////////////
     // Update our specification data.
 
     const bool isDataInitialized = false;
     const WebGLTexture::ImageInfo newInfo(dstUsage, width, height, depth,
                                           isDataInitialized);
-    SetImageInfosAtLevel(0, newInfo);
+    SetImageInfosAtLevel(funcName, 0, newInfo);
 
-    PopulateMipChain(0, levels-1);
+    PopulateMipChain(funcName, 0, levels-1);
 
     mImmutable = true;
     mImmutableLevelCount = levels;
 }
 
 ////////////////////////////////////////
 // Tex(Sub)Image
 
@@ -1312,17 +1312,17 @@ WebGLTexture::TexImage(const char* funcN
                       driverUnpackInfo->unpackFormat, driverUnpackInfo->unpackType);
         MOZ_ASSERT(false, "Unexpected GL error.");
         return;
     }
 
     ////////////////////////////////////
     // Update our specification data.
 
-    SetImageInfo(imageInfo, newImageInfo);
+    SetImageInfo(funcName, imageInfo, newImageInfo);
 }
 
 void
 WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level,
                           GLint xOffset, GLint yOffset, GLint zOffset,
                           const webgl::PackingInfo& pi, const webgl::TexUnpackBlob* blob)
 {
     ////////////////////////////////////
@@ -1518,17 +1518,17 @@ WebGLTexture::CompressedTexImage(const c
     }
 
     ////////////////////////////////////
     // Update our specification data.
 
     const bool isDataInitialized = true;
     const ImageInfo newImageInfo(usage, blob->mWidth, blob->mHeight, blob->mDepth,
                                  isDataInitialized);
-    SetImageInfo(imageInfo, newImageInfo);
+    SetImageInfo(funcName, imageInfo, newImageInfo);
 }
 
 static inline bool
 IsSubImageBlockAligned(const webgl::CompressedFormatInfo* compression,
                        const WebGLTexture::ImageInfo* imageInfo, GLint xOffset,
                        GLint yOffset, uint32_t width, uint32_t height)
 {
     if (xOffset % compression->blockWidth != 0 ||
@@ -2165,17 +2165,17 @@ WebGLTexture::CopyTexImage2D(TexImageTar
         return;
     }
 
     ////////////////////////////////////
     // Update our specification data.
 
     const bool isDataInitialized = true;
     const ImageInfo newImageInfo(dstUsage, width, height, depth, isDataInitialized);
-    SetImageInfo(imageInfo, newImageInfo);
+    SetImageInfo(funcName, imageInfo, newImageInfo);
 }
 
 void
 WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level,
                               GLint xOffset, GLint yOffset, GLint zOffset, GLint x,
                               GLint y, GLsizei rawWidth, GLsizei rawHeight)
 {
     uint32_t width, height, depth;
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -314,18 +314,20 @@ DataTransferItem::GetAsEntry(nsIPrincipa
     nsAutoString fullpath;
     impl->GetMozFullPathInternal(fullpath, aRv);
     if (aRv.Failed()) {
       aRv.SuppressException();
       return nullptr;
     }
 
     nsCOMPtr<nsIFile> directoryFile;
-    nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(fullpath),
-                                        true, getter_AddRefs(directoryFile));
+    // fullPath is already in unicode, we don't have to use
+    // NS_NewNativeLocalFile.
+    nsresult rv = NS_NewLocalFile(fullpath, true,
+                                  getter_AddRefs(directoryFile));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     RefPtr<Directory> directory = Directory::Create(global, directoryFile);
     entry = new FileSystemDirectoryEntry(global, directory, nullptr, fs);
   } else {
     entry = new FileSystemFileEntry(global, file, nullptr, fs);
--- a/dom/filesystem/CreateDirectoryTask.cpp
+++ b/dom/filesystem/CreateDirectoryTask.cpp
@@ -93,19 +93,18 @@ void
 CreateDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                                   ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   const FileSystemDirectoryResponse& r =
     aValue.get_FileSystemDirectoryResponse();
 
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(r.realPath()), true,
-                              getter_AddRefs(mTargetPath));
-  NS_WARNING_ASSERTION(!aRv.Failed(), "NS_NewNativeLocalFile failed");
+  aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
+  NS_WARNING_ASSERTION(!aRv.Failed(), "NS_NewLocalFile failed");
 }
 
 void
 CreateDirectoryTaskChild::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (mFileSystem->IsShutdown()) {
     mPromise = nullptr;
@@ -144,18 +143,18 @@ CreateDirectoryTaskParent::Create(FileSy
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<CreateDirectoryTaskParent> task =
     new CreateDirectoryTaskParent(aFileSystem, aParam, aParent);
 
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aParam.realPath()), true,
-                              getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.realPath(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 CreateDirectoryTaskParent::CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -135,18 +135,17 @@ CreateFileTaskChild::GetRequestParams(co
 void
 CreateFileTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                              ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
 
-  NS_ConvertUTF16toUTF8 path(r.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+  aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
 void
 CreateFileTaskChild::HandlerCallback()
 {
@@ -189,18 +188,18 @@ CreateFileTaskParent::Create(FileSystemB
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<CreateFileTaskParent> task =
     new CreateFileTaskParent(aFileSystem, aParam, aParent);
 
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.realPath(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   task->mReplace = aParam.replace();
 
   const FileSystemFileDataValue& data = aParam.data();
 
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -95,18 +95,18 @@ Directory::WebkitBlinkDirectoryPickerEna
 /* static */ already_AddRefed<Promise>
 Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
 {
   // Only exposed for DeviceStorage.
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aFileSystem);
 
   nsCOMPtr<nsIFile> path;
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->LocalOrDeviceStorageRootPath()),
-                              true, getter_AddRefs(path));
+  aRv = NS_NewLocalFile(aFileSystem->LocalOrDeviceStorageRootPath(),
+                        true, getter_AddRefs(path));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   RefPtr<GetFileOrDirectoryTaskChild> task =
     GetFileOrDirectoryTaskChild::Create(aFileSystem, path, true, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
@@ -117,18 +117,17 @@ Directory::GetRoot(FileSystemBase* aFile
 }
 
 /* static */ already_AddRefed<Directory>
 Directory::Constructor(const GlobalObject& aGlobal,
                        const nsAString& aRealPath,
                        ErrorResult& aRv)
 {
   nsCOMPtr<nsIFile> path;
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aRealPath),
-                              true, getter_AddRefs(path));
+  aRv = NS_NewLocalFile(aRealPath, true, getter_AddRefs(path));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return Create(aGlobal.GetAsSupports(), path);
 }
 
 /* static */ already_AddRefed<Directory>
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -83,18 +83,17 @@ FileSystemBase::GetRealPath(BlobImpl* aF
   nsAutoString filePath;
   ErrorResult rv;
   aFile->GetMozFullPathInternal(filePath, rv);
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
 
-  rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath),
-                             true, aPath);
+  rv = NS_NewLocalFile(filePath, true, aPath);
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
 
   return true;
 }
 
@@ -129,18 +128,18 @@ FileSystemBase::GetDOMPath(nsIFile* aFil
                            ErrorResult& aRv) const
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aFile);
 
   aRetval.Truncate();
 
   nsCOMPtr<nsIFile> fileSystemPath;
-  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(LocalOrDeviceStorageRootPath()),
-                              true, getter_AddRefs(fileSystemPath));
+  aRv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(),
+                        true, getter_AddRefs(fileSystemPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsCOMPtr<nsIFile> path;
   aRv = aFile->Clone(getter_AddRefs(path));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -161,18 +161,18 @@ GetDirectoryListingTaskChild::HandlerCal
   if (!listing.SetLength(count, mozilla::fallible_t())) {
     mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
     mPromise = nullptr;
     return;
   }
 
   for (unsigned i = 0; i < count; i++) {
     nsCOMPtr<nsIFile> path;
-    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
-    nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
+    nsresult rv = NS_NewLocalFile(mTargetData[i].mPath, true,
+                                  getter_AddRefs(path));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPromise->MaybeReject(rv);
       mPromise = nullptr;
       return;
     }
 
 #ifdef DEBUG
     nsCOMPtr<nsIFile> rootPath;
@@ -241,18 +241,18 @@ GetDirectoryListingTaskParent::Create(Fi
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<GetDirectoryListingTaskParent> task =
     new GetDirectoryListingTaskParent(aFileSystem, aParam, aParent);
 
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.realPath(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -95,30 +95,28 @@ void
 GetFileOrDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                                                      ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   switch (aValue.type()) {
     case FileSystemResponseValue::TFileSystemFileResponse: {
       FileSystemFileResponse r = aValue;
 
-      NS_ConvertUTF16toUTF8 path(r.realPath());
-      aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+      aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       mIsDirectory = false;
       break;
     }
     case FileSystemResponseValue::TFileSystemDirectoryResponse: {
       FileSystemDirectoryResponse r = aValue;
 
-      NS_ConvertUTF16toUTF8 path(r.realPath());
-      aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+      aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(mTargetPath));
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       mIsDirectory = true;
       break;
     }
     default: {
@@ -178,18 +176,18 @@ GetFileOrDirectoryTaskParent::Create(Fil
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<GetFileOrDirectoryTaskParent> task =
     new GetFileOrDirectoryTaskParent(aFileSystem, aParam, aParent);
 
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.realPath(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 GetFileOrDirectoryTaskParent::GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -266,18 +266,17 @@ GetFilesHelper::OperationCompleted()
 void
 GetFilesHelper::RunIO()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mDirectoryPath.IsEmpty());
   MOZ_ASSERT(!mListingCompleted);
 
   nsCOMPtr<nsIFile> file;
-  mErrorResult = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(mDirectoryPath), true,
-                                       getter_AddRefs(file));
+  mErrorResult = NS_NewLocalFile(mDirectoryPath, true, getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
     return;
   }
 
   nsAutoString path;
   mErrorResult = file->GetLeafName(path);
   if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
     return;
@@ -301,18 +300,18 @@ GetFilesHelper::RunMainThread()
   if (NS_FAILED(mErrorResult)) {
     return;
   }
 
   // Create the sequence of Files.
   for (uint32_t i = 0; i < mTargetPathArray.Length(); ++i) {
     nsCOMPtr<nsIFile> file;
     mErrorResult =
-      NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(mTargetPathArray[i].mRealPath),
-                            true, getter_AddRefs(file));
+      NS_NewLocalFile(mTargetPathArray[i].mRealPath, true,
+                      getter_AddRefs(file));
     if (NS_WARN_IF(NS_FAILED(mErrorResult))) {
       mFiles.Clear();
       return;
     }
 
     RefPtr<File> domFile =
       File::CreateFromFile(mGlobal, file);
     MOZ_ASSERT(domFile);
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -149,18 +149,18 @@ GetFilesTaskChild::HandlerCallback()
   if (!listing.SetLength(count, mozilla::fallible_t())) {
     mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
     mPromise = nullptr;
     return;
   }
 
   for (unsigned i = 0; i < count; i++) {
     nsCOMPtr<nsIFile> path;
-    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mRealPath);
-    nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
+    nsresult rv = NS_NewLocalFile(mTargetData[i].mRealPath, true,
+                                  getter_AddRefs(path));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mPromise = nullptr;
       return;
     }
 
 #ifdef DEBUG
     nsCOMPtr<nsIFile> rootPath;
@@ -205,18 +205,18 @@ GetFilesTaskParent::Create(FileSystemBas
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<GetFilesTaskParent> task =
     new GetFilesTaskParent(aFileSystem, aParam, aParent);
 
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.realPath(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
 GetFilesTaskParent::GetFilesTaskParent(FileSystemBase* aFileSystem,
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -157,27 +157,26 @@ RemoveTaskParent::Create(FileSystemBase*
 {
   MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFileSystem);
 
   RefPtr<RemoveTaskParent> task =
     new RemoveTaskParent(aFileSystem, aParam, aParent);
 
-  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
-  aRv = NS_NewNativeLocalFile(directoryPath, true,
-                              getter_AddRefs(task->mDirPath));
+  aRv = NS_NewLocalFile(aParam.directory(), true,
+                        getter_AddRefs(task->mDirPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   task->mRecursive = aParam.recursive();
 
-  NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  aRv = NS_NewLocalFile(aParam.targetDirectory(), true,
+                        getter_AddRefs(task->mTargetPath));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
     aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -311,17 +311,17 @@ HangMonitorChild::InterruptCallback()
     forcePaintEpoch = mForcePaintEpoch;
 
     mForcePaint = false;
   }
 
   if (forcePaint) {
     RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
     if (tabChild) {
-      JS::AutoAssertNoGC nogc(mContext);
+      js::AutoAssertNoContentJS nojs(mContext);
       tabChild->ForcePaint(forcePaintEpoch);
     }
   }
 }
 
 void
 HangMonitorChild::Shutdown()
 {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1093,21 +1093,27 @@ TabChild::DestroyWindow()
 void
 TabChild::ActorDestroy(ActorDestroyReason why)
 {
   mIPCOpen = false;
 
   DestroyWindow();
 
   if (mTabChildGlobal) {
-    // The messageManager relays messages via the TabChild which
-    // no longer exists.
-    static_cast<nsFrameMessageManager*>
-      (mTabChildGlobal->mMessageManager.get())->Disconnect();
-    mTabChildGlobal->mMessageManager = nullptr;
+    // We should have a message manager if the global is alive, but it
+    // seems sometimes we don't.  Assert in aurora/nightly, but don't
+    // crash in release builds.
+    MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager);
+    if (mTabChildGlobal->mMessageManager) {
+      // The messageManager relays messages via the TabChild which
+      // no longer exists.
+      static_cast<nsFrameMessageManager*>
+        (mTabChildGlobal->mMessageManager.get())->Disconnect();
+      mTabChildGlobal->mMessageManager = nullptr;
+    }
   }
 
   CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get());
   compositorChild->CancelNotifyAfterRemotePaint(this);
 
   if (GetTabId() != 0) {
     NestedTabChildMap().erase(GetTabId());
   }
@@ -2099,26 +2105,36 @@ TabChild::RecvLoadRemoteScript(const nsS
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvAsyncMessage(const nsString& aMessage,
                            InfallibleTArray<CpowEntry>&& aCpows,
                            const IPC::Principal& aPrincipal,
                            const ClonedMessageData& aData)
 {
-  if (mTabChildGlobal) {
-    nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal());
-    StructuredCloneData data;
-    UnpackClonedMessageDataForChild(aData, data);
-    RefPtr<nsFrameMessageManager> mm =
-      static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
-    CrossProcessCpowHolder cpows(Manager(), aCpows);
-    mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
-                       aMessage, false, &data, &cpows, aPrincipal, nullptr);
+  if (!mTabChildGlobal) {
+    return IPC_OK();
   }
+
+  // We should have a message manager if the global is alive, but it
+  // seems sometimes we don't.  Assert in aurora/nightly, but don't
+  // crash in release builds.
+  MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager);
+  if (!mTabChildGlobal->mMessageManager) {
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal());
+  StructuredCloneData data;
+  UnpackClonedMessageDataForChild(aData, data);
+  RefPtr<nsFrameMessageManager> mm =
+    static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
+  CrossProcessCpowHolder cpows(Manager(), aCpows);
+  mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
+                     aMessage, false, &data, &cpows, aPrincipal, nullptr);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext)
 {
   nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
   if (NS_WARN_IF(!ourDocShell)) {
@@ -2498,28 +2514,29 @@ TabChild::InitTabChildGlobal()
   if (!mGlobal && !mTabChildGlobal) {
     nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
     NS_ENSURE_TRUE(window, false);
     nsCOMPtr<EventTarget> chromeHandler =
       do_QueryInterface(window->GetChromeEventHandler());
     NS_ENSURE_TRUE(chromeHandler, false);
 
     RefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
-    mTabChildGlobal = scope;
 
     nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, scope);
 
     NS_NAMED_LITERAL_CSTRING(globalId, "outOfProcessTabChildGlobal");
     NS_ENSURE_TRUE(InitChildGlobalInternal(scopeSupports, globalId), false);
 
     scope->Init();
 
     nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
     NS_ENSURE_TRUE(root, false);
     root->SetParentTarget(scope);
+
+    mTabChildGlobal = scope.forget();;
   }
 
   if (!mTriedBrowserInit) {
     mTriedBrowserInit = true;
     // Initialize the child side of the browser element machinery,
     // if appropriate.
     if (IsMozBrowser()) {
       RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
--- a/dom/ipc/manifestMessages.js
+++ b/dom/ipc/manifestMessages.js
@@ -13,32 +13,37 @@
  */
 /*globals Task, ManifestObtainer, ManifestFinder, content, sendAsyncMessage, addMessageListener, Components*/
 "use strict";
 const {
   utils: Cu,
 } = Components;
 Cu.import("resource://gre/modules/ManifestObtainer.jsm");
 Cu.import("resource://gre/modules/ManifestFinder.jsm");
+Cu.import("resource://gre/modules/ManifestIcons.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 const MessageHandler = {
   registerListeners() {
     addMessageListener(
       "DOM:WebManifest:hasManifestLink",
       this.hasManifestLink.bind(this)
     );
     addMessageListener(
       "DOM:ManifestObtainer:Obtain",
       this.obtainManifest.bind(this)
     );
     addMessageListener(
       "DOM:Manifest:FireAppInstalledEvent",
       this.fireAppInstalledEvent.bind(this)
     );
+    addMessageListener(
+      "DOM:WebManifest:fetchIcon",
+      this.fetchIcon.bind(this)
+    );
   },
 
   /**
    * Check if the content document includes a link to a web manifest.
    * @param {Object} aMsg The IPC message, which is destructured to just
    *                      get the id.
    */
   hasManifestLink({data: {id}}) {
@@ -71,17 +76,34 @@ const MessageHandler = {
     if (!content || content.top !== content) {
       const msg = "Can only dispatch install event on top-level browsing contexts.";
       response.result = serializeError(new Error(msg));
     } else {
       response.success = true;
       content.dispatchEvent(ev);
     }
     sendAsyncMessage("DOM:Manifest:FireAppInstalledEvent", response);
-  }
+  },
+
+  /**
+   * Given a manifest and an expected icon size, ask ManifestIcons
+   * to fetch the appropriate icon and send along result
+   */
+  fetchIcon: Task.async(function* ({data: {id, manifest, iconSize}}) {
+    const response = makeMsgResponse(id);
+    try {
+      response.result =
+        yield ManifestIcons.contentFetchIcon(content, manifest, iconSize);
+      response.success = true;
+    } catch (err) {
+      response.result = serializeError(err);
+    }
+    sendAsyncMessage("DOM:WebManifest:fetchIcon", response);
+  }),
+
 };
 /**
  * Utility function to Serializes an JS Error, so it can be transferred over
  * the message channel.
  * FIX ME: https://bugzilla.mozilla.org/show_bug.cgi?id=1172586
  * @param  {Error} aError The error to serialize.
  * @return {Object} The serialized object.
  */
new file mode 100644
--- /dev/null
+++ b/dom/manifest/Manifest.jsm
@@ -0,0 +1,42 @@
+/*
+ * Manifest.jsm is the top level api for managing installed web applications
+ * https://www.w3.org/TR/appmanifest/
+ *
+ * It is used to trigger the installation of a web application via .install()
+ * and to access the manifest data (including icons).
+ *
+ * TODO:
+ *  - Persist installed manifest data to disk and keep track of which
+ *    origins have installed applications
+ *  - Trigger appropriate app installed events
+ */
+
+"use strict";
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+const { ManifestObtainer } =
+  Cu.import('resource://gre/modules/ManifestObtainer.jsm', {});
+const { ManifestIcons } =
+  Cu.import('resource://gre/modules/ManifestIcons.jsm', {});
+
+function Manifest(browser) {
+  this.browser = browser;
+  this.data = null;
+}
+
+Manifest.prototype.install = Task.async(function* () {
+  this.data = yield ManifestObtainer.browserObtainManifest(this.browser);
+});
+
+Manifest.prototype.icon = Task.async(function* (expectedSize) {
+  return yield ManifestIcons.browserFetchIcon(this.browser, this.data, expectedSize);
+});
+
+Manifest.prototype.name = function () {
+  return this.data.short_name || this.data.short_url;
+}
+
+this.EXPORTED_SYMBOLS = ["Manifest"]; // jshint ignore:line
new file mode 100644
--- /dev/null
+++ b/dom/manifest/ManifestIcons.jsm
@@ -0,0 +1,85 @@
+"use strict";
+
+const {
+  utils: Cu,
+  classes: Cc,
+  interfaces: Ci
+} = Components;
+
+Cu.import("resource://gre/modules/PromiseMessage.jsm");
+
+this.ManifestIcons = {
+
+  async browserFetchIcon(aBrowser, manifest, iconSize) {
+    const msgKey = "DOM:WebManifest:fetchIcon";
+    const mm = aBrowser.messageManager;
+    const {data: {success, result}} =
+      await PromiseMessage.send(mm, msgKey, {manifest, iconSize});
+    if (!success) {
+      throw result;
+    }
+    return result;
+  },
+
+  async contentFetchIcon(aWindow, manifest, iconSize) {
+    return await getIcon(aWindow, toIconArray(manifest.icons), iconSize);
+  }
+};
+
+function parseIconSize(size) {
+  if (size === "any" || size === "") {
+    // We want icons without size specified to sorted
+    // as the largest available icons
+    return Number.MAX_SAFE_INTEGER;
+  }
+  // 100x100 will parse as 100
+  return parseInt(size, 10);
+}
+
+// Create an array of icons sorted by their size
+function toIconArray(icons) {
+  const iconBySize = [];
+  icons.forEach(icon => {
+    const sizes = ("sizes" in icon) ? icon.sizes : "";
+    sizes.split(" ").forEach(size => {
+      iconBySize.push({src: icon.src, size: parseIconSize(size)});
+    });
+  });
+  return iconBySize.sort((a, b) => a.size - b.size);
+}
+
+async function getIcon(aWindow, icons, expectedSize) {
+  if (!icons.length) {
+    throw new Error("Could not find valid icon");
+  }
+  // We start trying the smallest icon that is larger than the requested
+  // size and go up to the largest icon if they fail, if all those fail
+  // go back down to the smallest
+  let index = icons.findIndex(icon => icon.size >= expectedSize);
+  if (index === -1) {
+    index = icons.length - 1;
+  }
+
+  return fetchIcon(aWindow, icons[index].src).catch(err => {
+    // Remove all icons with the failed source, the same source
+    // may have been used for multiple sizes
+    icons = icons.filter(x => x.src === icons[index].src);
+    return getIcon(aWindow, icons, expectedSize);
+  });
+}
+
+function fetchIcon(aWindow, src) {
+  const manifestURL = new aWindow.URL(src);
+  const request = new aWindow.Request(manifestURL, {mode: "cors"});
+  request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
+  return aWindow.fetch(request)
+    .then(response => response.blob())
+    .then(blob => new Promise((resolve, reject) => {
+      var reader = new FileReader();
+      reader.onloadend = () => resolve(reader.result);
+      reader.onerror = reject;
+      reader.readAsDataURL(blob);
+    }));
+}
+
+this.EXPORTED_SYMBOLS = ["ManifestIcons"]; // jshint ignore:line
--- a/dom/manifest/moz.build
+++ b/dom/manifest/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; 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/.
 
 EXTRA_JS_MODULES += [
     'ImageObjectProcessor.jsm',
+    'Manifest.jsm',
     'ManifestFinder.jsm',
+    'ManifestIcons.jsm',
     'ManifestObtainer.jsm',
     'ManifestProcessor.jsm',
     'ValueExtractor.jsm',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f4a62faddf2af0ee6fe6340efc1c73eefec58307
GIT binary patch
literal 534
zc%17D@N?(olHy`uVBq!ia0vp^(?FPm4M^HB7CvHNV7%+;;uumf=k2+ToD2p6M>gE9
ue|cVrvAJ+z-0gRh{(o?oGm3^R>XAz*P^#J|BfJL~-wd9velF{r5}E+F^Cvd|
--- a/dom/manifest/test/browser.ini
+++ b/dom/manifest/test/browser.ini
@@ -1,9 +1,12 @@
 [DEFAULT]
 support-files =
   file_reg_appinstalled_event.html
   file_testserver.sjs
   manifestLoader.html
   resource.sjs
+  red-50.png
+  blue-150.png
 [browser_ManifestFinder_browserHasManifestLink.js]
+[browser_ManifestIcons_browserFetchIcon.js]
 [browser_ManifestObtainer_obtain.js]
 [browser_fire_appinstalled_event.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/browser_ManifestIcons_browserFetchIcon.js
@@ -0,0 +1,56 @@
+//Used by JSHint:
+/*global Cu, BrowserTestUtils, ok, add_task, gBrowser */
+"use strict";
+const { ManifestIcons } = Cu.import("resource://gre/modules/ManifestIcons.jsm", {});
+const { ManifestObtainer } = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
+
+const defaultURL = new URL("http://example.org/browser/dom/manifest/test/resource.sjs");
+defaultURL.searchParams.set("Content-Type", "application/manifest+json");
+
+const manifest = JSON.stringify({
+  icons: [{
+    sizes: "50x50",
+    src: "red-50.png?Content-type=image/png"
+  }, {
+    sizes: "150x150",
+    src: "blue-150.png?Content-type=image/png"
+  }]
+});
+
+function makeTestURL(manifest) {
+  const url = new URL(defaultURL);
+  const body = `<link rel="manifest" href='${defaultURL}&body=${manifest}'>`;
+  url.searchParams.set("Content-Type", "text/html; charset=utf-8");
+  url.searchParams.set("body", encodeURIComponent(body));
+  return url.href;
+}
+
+function getIconColor(icon) {
+  return new Promise((resolve, reject) => {
+    const canvas = content.document.createElement('canvas');
+    const ctx = canvas.getContext("2d");
+    const image = new content.Image();
+    image.onload = function() {
+      ctx.drawImage(image, 0, 0);
+      resolve(ctx.getImageData(1, 1, 1, 1).data);
+    };
+    image.onerror = function() {
+      reject(new Error("could not create image"));
+    };
+    image.src = icon;
+  });
+}
+
+add_task(function*() {
+  const tabOptions = {gBrowser, url: makeTestURL(manifest)};
+  yield BrowserTestUtils.withNewTab(tabOptions, function*(browser) {
+    const manifest = yield ManifestObtainer.browserObtainManifest(browser);
+    let icon = yield ManifestIcons.browserFetchIcon(browser, manifest, 25);
+    let color = yield ContentTask.spawn(browser, icon, getIconColor);
+    is(color[0], 255, 'Fetched red icon');
+
+    icon = yield ManifestIcons.browserFetchIcon(browser, manifest, 500);
+    color = yield ContentTask.spawn(browser, icon, getIconColor);
+    is(color[2], 255, 'Fetched blue icon');
+  });
+});
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bb87f783b7b21b00fa4a294a9678d65b21db8448
GIT binary patch
literal 8156
zc$@*;A0yz2P)<h;3K|Lk000e1NJLTq002k;002k`1^@s6RqeA!001AONkl<ZcwW_;
zX^<S}ecqow=boL}ot?d~z~Tl)LIf#GqGU^gQnurY9SN!&syL}4mC9u&RT3-yk~kIG
zPUTxvlyZECt(aulk>bcMN@ZJ>L?Qv|Bq<&MNq{&AV6g}GJm&1_yYuMZ)74W`SP}wY
zyZo}Adb?Y*(C%-a|M9+WuY$j#@x_aF1;7ak{kbyqk+W0-)W1HK<@I09Q2r8u5CTpH
zQS=GN3r}`!uVOnMY(IkUIOtjqT-!nDdC*nB)BuDb3Z(*ayvbzDGZWKwg2D^b8A5CP
z^#+tedDID_Q*FneYxg`vegGv1uw{AZxB)z$5L80qb5n`1)94@~7*O4gg#uh?P6s`H
zHe>22jE!M>?+nK4)o+Z96uwDNPye+8L@=j<2%m4e!O2F?M~ChRJs*vpi>zs2qwB)q
zz3T-6Cgcpx3Wb38zK@V|qKpP~d#*Ui?X*!1T5x(@965RbqkE<shmPO(WdL9Li*S{4
zkHMVu!|17o6V5g07RT}62O-+7k1ioJD2MU{3?UFmM1d|+IjcboDe!%f7xb_Dpp-Dc
z1mU<FE8=}sQJ|_Sa``NdK6Jcs<l*CAE{>G%DJbP0ftjOYo)X8{1mJquYCCj@k2O_8
zhqL1c;+Uw{gU@FmxTr6MA|Pl!=h#64&~sb@@<6K*P7H7^>jGX6YYPj|`j9k2n%p-F
zm$rTI!w)u&eBi|6_bT-CdjRJ#JBXg#?D>@rUE|X&mhB-7>4dC~<u08>00BkE08oKQ
zfeJ<bivTDQpe3|<9Y%Z(O4AVn2nawSES(UqHX_1Qp&DhFUJGztfbbSBUB(P^)60MU
z|0>tty7<&@p1JYq(aDL=-L0FHyDZaB?l{3?J3YFCuIbs7<6$(X;|({$lI^2SNBDGx
zX4%L_A$;8sP+B0NB_XC#21NjJA5fl8*mPbL_>$DsSmGpF6oGBwpt6Id`9;j`pT*MR
zGE_|!=~SUq#7BPRmm0?&`QT>==?w00sCNn{jBt9T>p!+-`Dj^=gcPe3%BG4P%f-0w
zVlMP>5QNd{AZw^-nI;@UilPWn7|~_Crc_Fi>*P7-AOb%KmDd{GM|c#0xfJg;O+&jp
zMIdzP#dlBy5OpmCluNZXxA5E_{;$gE|M|a9|HS2gS%9Q;GuE%KbiETTn{X^wz-S1e
zC=q_V*1=0#7GCAN)3MR?Ld@h1Y-&0J0Azq@2)Nuwpa_NnrNnS3!sv@&Bw71Z;5b;{
zY9U`KBPbNm?evf(Or>K%A)t`*s5f8u9-jTue|_rTe{b;%f3Z0zg>!n<@++1bfH_CP
zq3iev>5L`IgB=3ZjE;OYhpQg2O&0{JE`3d?fRel;O5sEa9E21ILm&y52s&r<^O#SC
zlxek*g@>`)2s&Fkh;kY9d|$#fa=kb<bPb`Y<Hp%H@aL`8lfV6=m3>cqWa{yI?4UHP
z*zi3Y!|~yHJ~{-`ussQz8$CWFj6;<yn%NBIJs|5iP*ep_Ad<u@GbR5eV3H@vHJ~Ie
z>N_bQq(pkRfqfYdlT&qE*@;lww;v}ya2%y_3HhmUK+^z^pezR=9TjzZSig7$-+c1F
zeBn2rzxLQY29)NUb<3X<=h(h<!j=~xZ>U)2eMmS3O%WIAstSFl4MmA76*xoS43-8Z
zx!%1}&}~CQfF2q(;kP?jKl?iFAIo5)*@Ir2!h?h~F;<0=&jN-{*dZdvBP1JvK)Q_Q
zpZbHR7zwBDwS%5qv-}f1FA&#s35i5-Up0drKj`O-5Vf3+DlJ{GdZ_0ObaL5*%`GTI
z=`=i|H1tG6qrJL{o$Uq?__+4kSxlpgB3w*N)L@t<KJ$-0gHnAIieW%T5Q>`uf?9a(
z9G?5iQ-oxnxEFw=&-qCy?@AXO=bNgb;OD0Em@eqprhAIIiV?~tyzT7{3V9P7w0oqA
zYla;_ArZl*P*Pawb0q;KQ-pmvA8>35zd|7|5W3A4E`I-IJo~#}#pT!D!n6P5kMO<+
zj$vZ&6cp3w6OUpyb#0tuuXO(O3s2oGAnA}TKYVh>j=^wUg}ep{m1h?_cyYZ8B`(<#
zcH12XM{0TWOfzoqs4xXX7!=T-6CS)y%rsERB6*%ti=P1-Q4YliPN1;=5X`YUvXxQP
z59~*`)rO3aARHGjefxV@TU<f0Hj3i-SQ4JnCJ+)%f907IkN?T*zjarDf(Y!8114nw
zBo(B=>x)en?FiUVbQtA=tjGxpbF5sAViBzn@DVGF!z2Y1QbXmOO6a6De*k8bN*2Cv
z-|S%7HL>V1#MKfm^S-4PF*;F8zgAJ{FqZ$FCW636rdW{XkwcnMdt(Pb{OZ@A_~*ZO
z`ox_8l7(l>4Nk>*#}f!!HZ7_u==2wfHVNvy&Swma7fft=5!U&67ZDr+@WK=XLV*)k
zrSw?}D1|0Up(gGbgql{m1PC+(Zl-|ogGZ39&p<C#F*-Ga`qVVEY#y%b0-7pSHG#D7
z<JV!a{NGU@#2@a7t{XCR;?8xWM@Wcpt>;TSuOJRJ-*V7wbrEQaIKeJ4#0bKn{3ry%
z6ol5-;4`&9i)n_N#Za6{j#Mb1L<$n9=}gO@)A7*rEg<j+AjI;GWhlN+ej&l_$NHr!
zIPl>8lH7MTcTi#YUHs0OlRq)TZp#sUH}`DX!O5QMLytnN6H?prQKm~ALet_LA&bgU
zC8TvTa0qQvH_#0u0t(?!Q6B^XSx!)qD1o4bY1k#76+l8Gb{feDASu~{KzN^kN)---
z=YT7N0QTAzs#6nKzp;coll%6~HE5X(TI*Z*51)SQ2>`bQr0(XPp7c8&mf8+BY!@v*
zlvR02SK!i~JwmbwVbL&fjn!m6lf{;*!XgZt3JC-h;lv$rARVz>L*^8kk^o4d3Q(mF
zCFCqFl4qh6NCE`{HC6&=AfH+74$5Pr=&<4i{r<}Cc9CPA$~x9b?f%D3-j0K$a&6N+
z+g(=@bBiwOcmY%e*y%WEJ08YO6{~t7sl^B=KL#t^bTkA<4=8~YO6HF!jY<GTeJL>n
zft5H*;K&?dJZxj!aN*}n1d)z>v5Fg47YAJg4CXTdn8I4QGLK4qT-I@P4~p5GBycqf
zkoTQO+jP<GgxKesPJk!~#08e;!}0@Z0$1s#$-IG1)<l;8(9c6s(i1y}q5(Jo6^cMb
zse9rO;~a=<1guCE1p+tpJw&d9nQVlN#qN22nJ{D5j2xUr$%xSPQ#gZA(h3-e%xo6b
zY8fN7D%#Bs=HI@CEMeEDC*W+fAES#N56(Z`cvnDOFFX|z$f%)Wz3sz5T#c4HE@oNA
zuaij2rvrSFCq$31(gtuC4i!W)D?#F_K%7L$voK5@7s+P=0Yt@a>UE&`E=q0-tyU9A
zK}eGQ(8mwq#aAw2>B?e1<OFtdEpD7Uwm;so)if+FuE;NB;mS4CC+pZ;--g|Dkps{%
zvZn&z>D_XWtkx|roNM(w6m*D-c6hDQ0cC}dc+Ju;00LD(8L=xspox_JW|-lJFanC#
zYT_PWz)%7yc1_|eaaWMIOW;Lz2P54zOjmM*(?OSt269Zd-No1c`&Z#C-H?s6!O-F?
z8W}5Nb$$sqE?$Mjum8ut@y{^6XG$vb+Vbk)dJmbv`}BJy>62|YkQMVgO-HC#at1Vp
zm8U8Khfbgfq37Ue=tFTm=q&RJfoOzdh(eeGiEt?JIudZHd!#xEltclLgjYYjb4q~O
z=^FB?FHqt}FU>P+3(GREvm)i99*V`BI47-k0ZdInr_n^<d4Q_mt(RWsa^y`Uqfjm)
z8AceKde<D}2GJ)h+mk9aW2$&%tBZ+h9@p#$L_^q-0EiTz(CuN_?qN^HglgGXQxqtw
z3N<O;p*Tb!V8AuHoL<DwqeT5v3r*Y>yU3?@lpHp8;vBVi_Att709?KxPGY#gF!Tf#
zJLU%RzK47+&+A_D9IBZChwLnrsv~&)x$leflA)Ofl3@lOG*&a_p)<Dx)bqm=4qc*o
zK9+pK$QsfuPlg`W!oC$9SFBrTx20mic2Ou~VE~|8(mWKGY;oLRSS4^`aD8_P7?D5<
zC|_oRQgs6Ju)J+!CGap-+XHm3U~+B-E0?YgmUdVy;SoWVn1n~iYFK(_9_@{7Jn(ZL
zLT7Uemw$W?!vkTu5Y#|-N8!l;ICIMo^ZanmC7cz@!}%=>O+r~{^q_m5{N^=Er*j@Q
zLRvOJ-qg_1O|<g`s9u_fqW-M$neHd*C*{6d;P;)=4?#^Nu*5xqxG11wd&mbCCZjgY
z_9j{#7h?wxqdGc*YOOld_i(0*t7BETjw>N+bu4VMYpx$Xgj)^Iwi#x5IfWVF*U?1}
z4&|V<d+s>C9O}9KlLhqifk`_TEi29?g&fXWd|n^LMz#ngpF`X65hY-}0Whu$BL<r!
zX>pD~419qB&Iud{94d&VSuGriwlUFNMQ`ysMuRq{MhnPhbf`uKJM4lnyp9WeY_Dx#
zoe5Z;arlH~05CF{go~W{b4Y$_Zf@i7gU6s{Gcd!TvTKr-qhQMO<OhzI2nw*Wyvcv0
zfuPw&XuA^6UE2eEp9<(6KZcTk;q%c>1IE?{RB?)&i4l%KOq|2(J{-<U5?X}l_pb|t
zAJPfU?WVXy)ilgZ6mj(U5fs(It~r&a>v;I&BN(lZVc(%yJovGn!N~Y1-ul7IFwHch
zBzZ{BWo6EkO3-BATsgU0lCD@b9YipJ7fJ|(wCjYaVv$amW3tu^9cxUGIv_{Fc5$S?
z<^dGnlLS4RXV`79s`w6idP)AhC`_9~!V!T=;Hcu5_^8<nV>qBXu7~1C5$h{!0wDC5
zgP8AHH|DWHhwMMR51nQk8><@vFJCRAT*zbX+9DqM<VU0nzwpDC(O6lR&)sVHJsVSp
z=3o{I*x=_hJ%il<o$v{ViVy01N&^~}jr~(K)QTB-O{J!Vc=h5Es>Lj784XsXBIvYH
zf-mvD!q1mX4YmTP#bWG40Yil`93`z-DIA}(I4C|WR6}2S8v;mH>ym7z56t5EKYkNy
zTy$2)DsU_dnx?|H9dsBf?e$F*HVW8YU87@6Ec0^`4S?(kIh`(shsw{@r^e-)NqSd6
zl6NR5lvEWc2nb-k<zT&GW0LOhSS4mEc_>i0GG0c3FdWN)8%cSV|AQ=H?gTn)Jttw5
zI0u~5Y9*iqikHm(U`2}|d4&qfRU5NM4`S)Y3ND|&f=V$5g-{whO@yw~-vvlTs8mY;
z?0?^3EM8v}r&LBOSQ`LT;(f1UVdL5YzPqvY-Wz62paKY#>Zg56KJ>-jH(AkUszsQd
zE4G~;$)jxQvJ_B#4+<4g)sX_!YE52w3H?|KrS+v#AOKGk#EPAhfT3cDD@_j^m_K)(
zZtB2rEG#pGs?`yJnXbPE5;Z75dZqQno6JS~<bIQ(Sgedlf*!5c;PxE&t_#<;Wsj>+
zDZeYA`5;N6{nt+vT76XaG2(adV9msVl7?+$FvFPr@Ph{l(?e~vh(mLe7$p>19rqvE
zi|WjboVXGeK(HDmVES`Fu~PQs!ykg93|wmj<?C7G_UwmkduVPo;C8!GsiyW!rh7<H
zIwykwB^l3OyoUY9k4bJDo!bM|Fpy<6c6vP|BhL!gZM5EXyJJ3X;Cw0u6anaTw$NoC
zq1rvX|L_d7VhI-(ckro)_hFUEv$fs8^z=AJOyKQq2Xp%-@!cC8af~b*`ckMc>2YLE
zs}(45jm!dvV5lI5)eaSOS2n~|hZt&?Up*(Txp{p7_31jSZV&CP9RV~rR9%OjF$t8e
zV;G(K^~ca=ZaTNVDWyFf3aqvzSDvcv3JArlj`c7(;th~BHA&1GozQADv9O>r(KT^w
zyo&Re7HHKxu3lL{uie7hMnkOr?eDyZe7S^5wg$`bVlaFj1WEIkT&FKZ5lB1>6wV2p
z^f?vhrdFeqcI%w;`5fxAQ@HT*IVmE8BbUpd#OAN9Dy)Vj<mj3o{_quPA_tcnrAk=}
zjJQrwb_H}6h?2FD0P1xe9AM6Auu8R=_}-vf9yq)QFI|~uXqB<BxQ4&+^N--AAG{{7
z$5|Z*oo%|!a}fD!&_||WLzjhTFepNR07(E*DTpCBNw@_7uY0z~ic?3k*%D_Q{@6z`
zHdT|Pyv$_mxt{Pl^1xAUfn1Z7eA4$^dG!r!(uwSvQvjwZP6M3e5bEqLa}YPJ8^xU2
zKqR3ci8b(CdC^kURRsM6JioAt5jtm`39nWy;qti)j9(oig)CG=06){-Y0_0~l-U6n
zTT7@Lj^vs+-+1B-jZ#zb-lI7C-mT%pP$f@nZFjjAo5cek|1ff)hqt-L`kR0Em-&4a
z#Zg&9$0e@V6PFbG$#<2>y-+D(aO^vJ02qi&Ur~48Fw<1!tP({hL)okfWNEPE1Lamr
z@>1T^Q6!8@Z(R}A>(P3$b0-P<*pYpBoBtavgNf-HRu@<0T5W8iICTh3H;|+&IVo`u
zCC}vEAACLp$4?fv(wy$x+=T5i<W{#(WT*V>SH33B?Cdn9N)3)qoP4EfwiyK+5^~Tq
z6sr~7xHOMs*t+?`u7IlL+zSe#lZfKRPIFy&v><A;sC!KmHQ*{Mg3|vc5v2;f2v;t=
zgF}b*;GGLsrE#O1WPhgDa@h5%c(ZNGy*dOZxkdtJ@EIZTc?u_t=PpCOyR##O<dY8^
z!kf?C#MJ)10!N%PG!#{l*JRQJZXi?0Nrj6LVQTLT7MIo})$9srbR>VKm@}T(CLlEm
zr1Ldx3yNW4|GpW#`SNQh&_OD6`347%lnGTr^6FccQ7#uTH93aurJDi-ih>=RFgyne
zApf?SIw@6@<|Bnd8d@ow1Q;bt7>)~PFUZ{9+D_pNsW>)@$n*N{DNEI&Bm!zvbo;_h
zxJDKnXLfBAnTO6y)JhE<A@nGaQd{&a>@9fsrJsEt>Z4^jKCx)i5_3*DXP{*2(j*S5
zyS0UhS|#}*aD<D?yh2A3fG)WvmP#oSMzNB-q)0ePNF|bI(V&|+Cu4L%mI<<znk$#m
zUBXn71{i8__iQ->PEzHaZ3!>UJo}!T_SuZ_4PA*I)9DDGknElVyW!%ue&@g8z;wNz
zR6X>zw`2Ecnsm@(6Jy9SK`vjtA+X<k<qhoRx@m1`MZRgPr$CQGuI~=cgW;Au7lo;F
zWKJ~%N7SQpIB?_gb(Z!lUDUzwXtD3P|DzvdSQYWc3qL|K3|*J@AW&4en*Pjt9vmJ!
zI{VE^DH}V8QdI?wR#)6W7M%T9z9)(tTkf~mG~WNdgR;9=VRfU^B<_uD1_!ww^44#n
z;_VP-;+npT64y|vYkW~8ZW?rwKMY6iugAX>sl3os+-kh<k%v*9nHq#6peyyNR7NIe
zaD3xE0Wr+JF*7qhZzvH|#9=XF@S69XR{ZV{{Vl*!#d+oj-}%1m7dJMxW%g|wKl6!4
zFg`hsb#||#<t(P^WdS4G977?MDDso*#6fZ|;W{{~GZQe&MR~I|871bIfg$Uw`#*F7
zJ9LoKv#^I{U7lCV7^{!c@eBN#5m<WW48v>ww$A1!C#ugFDxRP}5FnJD)JoQrHzazt
z3m_5^Ols9!PN2w$E1ZN|j$e1=Ll5DX|Nh^?J8xY?<Hkk|g^&}tsR(3fro>6N8l|j>
zjq7n`QcRP{whl#sR5C@=Ww($=JvjgJS$W1*1f^10oZ}N#r0Y;M1KT$5v)VJaeKtS6
zcj`+M^=d<-YcxdYX$IPB>*!cLR;WUHloX{unAdIrLqBwVvIebGMsITyi`-zhEe}0b
zKPBjgr#PhV4pACv89hQq3lVrWeAmYK)HsHR<$5@9{}H_ZV;^GE_!0br-}o13v6P2V
zByczcbka$ynl3wtM;>`7&P&@(d5y<1GV^rL)3*yqy6Ts{_1u@G<CV~|FtdLaInTv@
z9`|y@eGruZ$>*{u996}^V+SNzUi#jP_=ErWWvnbL<IsI`3{eB{`!IZu8U!Q2q+`$I
zGYsi=oL2zZV;&xxY(y8h7QFD%8(4YgIxfBZIu0B^DtkOK(&dEAbrupsg?z49F3K*X
ztDA5%<F|k6_k(!O^d-9H^QEy$Mb{KKW&zczi;L_z_H(ayV`&wU=SzYs6!HQ?4)<8G
z>dXyS&tL90kwA|2?yRnIEp!eA69_Xvuj2#p8y)E^iqFy8Z7XT|q*4}Hp1X}Ub{mya
z9zXcgZ;NAwbC6CDQ~QtMmD6d0?sjc)RMX6#75Ci9-)a;-{OhOaoM*!zfZgfH+v_Do
zL3uom<27y}=)5-+NNx(nNf}uEW-S_et*%rVkrlPwwP^Jsnw$MCiWrj25$761)3R-O
zA-q^+0<~;*%`NOUvUJ-B^G}nNlz`*_GuaE8I=Bxj{VJy#x~vfDGn2At?PiN;8@W%)
zLE#+$5{-WR3(t;EkDuxhj^A!WbpjOV)3KQ`<V+pc-?%6*H%u@=-n?=>0kX>+K{E`D
z)v9tXevogXE?!$k=(+;t4u`m8oE2$xahZ%GE@bzJ>oWBk7GHf6q36m@=`_>CH9A{S
zHH2mso4JuM5zgax^%^jD@IKev*lrk_62DTbLSp>B!~1dM;rHX`Kl^DM<mp25rYuZF
z1-c};E*1+iCnv3A1UYl$AU^vqAB9gShmRk^fe$<&Zo9)FVLs1tJwCS&r+)2G?7Qy}
zuAV)Qh!xCgb>%p#I#K7M^tx0nMK{sR6y^c^_C5J?DDD17PfkvcpVoEA8|$jo!+5oX
zlYj4Tv!mX^i{JUKECe;fM00IpVEZ7F2*N<9DNN>{`lr8&oAb+9eB(Uo)023MWf*ey
zui$RSX9#|K`6i5f4&V8sKSQxpl<t~GI^t3V3Yyts!_rNL)-z}C1t5h|b0<FcSZ%!W
z6;>X=BA{aY*T^?+tYPxtG%_mWJc<W!C@>GXwk@0D4reBDKvl(AG$W3)ahpAQ1lO0>
zarMPl=)N`Fi!rf(27l-AU&EQFzlIC1oyXuv-XO8^WwbTpp9F9J;nM&@D!Lm$0*BJ1
zS{{G&_iAIsU)4Pq==Nmsy0);6@x2pJ4HcRGDMDwv**DDYVY7#O%Mbm^FXG&{p2JOs
z$L$Pp;*iT~?2l|Y>-X`&Pkxk5;T!Tj3@#^_b7Z#7JQ}+GZ-R?o`_BM;DtIqQ>U+XT
z0U7R_&wOEXqw#egG^(LtZFLh-;9>2Z8}OPP+1~7Sx`3+e3MbjDT4LiE*$$1R;dX|Z
z%^+7PVtn5e6cxx8vTPn}*jimji<+LU%bTkLM_9@MguvnG!!iDtaDE?vLFts%XQ14P
zgAzD8Wl$!SrE<o7pZ((aNbysKYa{6N5cX{OL2EO0*q$eAF!n4`Y6pKn!_cMlGCyFJ
z`%9B7!9RZfC0TpD+b}XF6o##}zJcMv_Y38h!ex%^^YrNKd@&C?jn}ro>6kgReCvzh
zwLkq`02XCa4&_mP>LO}rXuE?;!)pLihRTJT=U=kT317>Ne9+YCrYQYWLf>^{Q{7h-
zc(y%wYAYM+3L%*U<a<6^n>$iv-fK)9+Ka7)B`Ap3S($Q4UVmXymqn=OxZscjf%=|F
zIdS#f03ji<TA^dF|EF;N|NcoDR-$kSOmrt9HY_!fckTatb93SK-)V%J1tw+w;Bs_g
zX0oul7G2eoK8L?yD7qde(Y@0+#)T_a+{hOS*jm}d?F>y-@C*O&GnirC>JdhVP}Y`K
z<%n6QGi6Vz|A)NW8x1ItBIjG%Ms};a{_4LC7oI;2Kpm2j=x+WbGrGkbCUdlL?tH6x
z{ogrrzwoar+3W!mHrniX^Q=~d(F)erHlflj$3OOAL<IKME9ZoBm#%Rv8@P?Kp05-M
zA&Vbz8Nt(%{+ap+AgxsZvo?m1;nighv^u>sqyqcxaQ<7r54XKE=#*jIVHHaqGZbD~
zDo+)u5;a1NQe#T(_#?&Iv0p4jQCW38Ldv4f&h;IhMeRF$0F6cqf$PW_l%l44+V3XA
z)UlW0b&2c0WN_&rYbc|CVmi2eYHKE1n%ed@UjMUb=gR8<wx}JdLA9vPE;%UJ4VH4d
zC7v>G0jb=FR25K0VMeV#^s|NH%=?R;Yie?O>i7Tl3$jP^(>8<qj?Q6wbwds=rwQED
z4b1c4QpNz7uBN$8cloVw`TM5<Y!1L_4#Da5;kcCdUZq~cPdB2W>#(mt6nb{lURZLQ
z*RNVeHuSUAimI8W*<UCY7#g|)d5i6Khwu96w7LWx%G(`{_*_f&kb6c34PCQ3&C9QN
zD=$7DHLub&SnmVc9)QywhBI8f?z)o}n8PJMbyI;V^;M=SiL;cE0|&K{1IMzN>TD*0
zVIn|{pyd6+pK`dGA<4Ig&~kg5OHt$UrKodrF%2V8tFO)w7<<4q{!k^uT>wfU?NauW
zu*;>es3cD%Go=(~rm0!g92qx^{FtHTswyIA@rzeOF!bao7=hDVcOt(9r?DAzmY1lT
z0NMlUh;9wV8Ok^QP>ILg0oomuPHCy5M8m*_l}%>>Yf^?%Jv@yFDiA5jDHaq&Oti}q
z3Tar_ef5TwJRbn!4m(CPRK4ygpkYwEz><WXfZIjJP`gh-9dK(jnA`f0yaBnp$V-6i
z(iCDBF!vmgXqP5Z8<L*R>Gc2<ZI`Ak90oN6EK!imx7#J(OF(x5TVM^(yGR*Q>YzbL
zDJaq4{V<H(oN~_r-Fs*v>;~mFI{9d~`}EnpAO9cKQN((|R%Y`60000<MNUMnLSTYs
CFib%J
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e2ce365c3c7c7ec86e5eb0b626b908d301e1e0f6
GIT binary patch
literal 141
zc%17D@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nET5KkA!kc`H+=Qr{)7;qdg_*Xw`
z-bIee6LROzy=i^+oq@x{W<r$0R)?&{D@-d{gSeN7dFbL5c+K;Kaf5N`wC85qrUA`o
N@O1TaS?83{1OS%jFC+i}
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2803,21 +2803,24 @@ nsPluginHost::WritePluginInfo()
   }
 
   nsAutoCString arch;
   rv = runtime->GetXPCOMABI(arch);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
+
   PR_fprintf(fd, "Generated File. Do not edit.\n");
 
-  PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c\nArch%c%s%c%c\n",
+  PR_fprintf(fd, "\n[HEADER]\nVersion%c%s%c%c%c\nArch%c%s%c%c\n",
              PLUGIN_REGISTRY_FIELD_DELIMITER,
              kPluginRegistryVersion,
+             flashOnly ? 't' : 'f',
              PLUGIN_REGISTRY_FIELD_DELIMITER,
              PLUGIN_REGISTRY_END_OF_LINE_MARKER,
              PLUGIN_REGISTRY_FIELD_DELIMITER,
              arch.get(),
              PLUGIN_REGISTRY_FIELD_DELIMITER,
              PLUGIN_REGISTRY_END_OF_LINE_MARKER);
 
   // Store all plugins in the mPlugins list - all plugins currently in use.
@@ -3003,17 +3006,22 @@ nsPluginHost::ReadPluginInfo()
   if (2 != reader.ParseLine(values, 2))
     return rv;
 
   // VersionLiteral
   if (PL_strcmp(values[0], "Version"))
     return rv;
 
   // If we're reading an old registry, ignore it
-  if (strcmp(values[1], kPluginRegistryVersion) != 0) {
+  // If we flipped the flash-only pref, ignore it
+  bool flashOnly = Preferences::GetBool("plugin.load_flash_only", true);
+  nsAutoCString expectedVersion(kPluginRegistryVersion);
+  expectedVersion.Append(flashOnly ? 't' : 'f');
+
+  if (!expectedVersion.Equals(values[1])) {
     return rv;
   }
 
   char* archValues[6];
   if (!reader.NextLine()) {
     return rv;
   }
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -4073,16 +4073,23 @@ PluginInstanceChild::ReadbackDifferenceR
 void
 PluginInstanceChild::InvalidateRectDelayed(void)
 {
     if (!mCurrentInvalidateTask) {
         return;
     }
 
     mCurrentInvalidateTask = nullptr;
+
+    // When this method is run asynchronously, we can end up switching to
+    // direct drawing before while we wait to run.  In that case, bail.
+    if (IsUsingDirectDrawing()) {
+        return;
+    }
+
     if (mAccumulatedInvalidRect.IsEmpty()) {
         return;
     }
 
     if (!ShowPluginFrame()) {
         AsyncShowPluginFrame();
     }
 }
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1201,17 +1201,17 @@ PluginInstanceParent::EndUpdateBackgroun
 #if defined(XP_WIN)
 nsresult
 PluginInstanceParent::SetScrollCaptureId(uint64_t aScrollCaptureId)
 {
   if (aScrollCaptureId == ImageContainer::sInvalidAsyncContainerId) {
     return NS_ERROR_FAILURE;
   }
 
-  mImageContainer = new ImageContainer(aScrollCaptureId);
+  mImageContainer = new ImageContainer(CompositableHandle(aScrollCaptureId));
   return NS_OK;
 }
 
 nsresult
 PluginInstanceParent::GetScrollCaptureContainer(ImageContainer** aContainer)
 {
   if (!aContainer || !mImageContainer) {
     return NS_ERROR_FAILURE;
--- a/dom/smil/crashtests/crashtests.list
+++ b/dom/smil/crashtests/crashtests.list
@@ -19,17 +19,17 @@ load 555026-1.svg
 load 556841-1.svg
 load 572938-1.svg
 load 572938-2.svg
 load 572938-3.svg
 load 572938-4.svg
 load 588287-1.svg
 load 588287-2.svg
 asserts-if(stylo,2) load 590425-1.html # bug 1324669
-asserts-if(stylo,8-11) load 592477-1.xhtml # bug 1324669
+asserts-if(stylo,1-27) load 592477-1.xhtml # bug 1324669
 load 594653-1.svg
 load 596796-1.svg
 load 605345-1.svg
 load 606101-1.svg
 asserts-if(stylo,2) load 608295-1.html # bug 1324689
 load 608549-1.svg
 load 611927-1.svg
 load 615002-1.svg
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -14,16 +14,18 @@ support-files =
   test_largeAllocation2.html^headers^
   !/dom/tests/mochitest/geolocation/network_geolocation.sjs
 
 [browser_allocateGigabyte.js]
 disabled = Does not reliably pass on 32-bit systems - bug 1314098
 skip-if = !e10s
 [browser_autofocus_background.js]
 [browser_autofocus_preference.js]
+[browser_beforeunload_between_chrome_content.js]
+skip-if = !e10s
 [browser_bug396843.js]
 [browser_bug1004814.js]
 [browser_bug1008941_dismissGeolocationHanger.js]
 [browser_bug1238427.js]
 [browser_bug1316330.js]
 skip-if = !e10s
 [browser_cancel_keydown_keypress_event.js]
 support-files =
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_beforeunload_between_chrome_content.js
@@ -0,0 +1,144 @@
+const TEST_URL = "http://www.example.com/browser/dom/tests/browser/dummy.html";
+
+function pageScript() {
+  window.addEventListener("beforeunload", function (event) {
+    var str = "Leaving?";
+    event.returnValue = str;
+    return str;
+  }, true);
+}
+
+function frameScript() {
+  content.window.addEventListener("beforeunload", function (event) {
+    sendAsyncMessage("Test:OnBeforeUnloadReceived");
+    var str = "Leaving?";
+    event.returnValue = str;
+    return str;
+  }, true);
+}
+
+// Wait for onbeforeunload dialog, and dismiss it immediately.
+function awaitAndCloseBeforeUnloadDialog(doStayOnPage) {
+  return new Promise(resolve => {
+    function onDialogShown(node) {
+      Services.obs.removeObserver(onDialogShown, "tabmodal-dialog-loaded");
+      let button = doStayOnPage ? node.ui.button1 : node.ui.button0;
+      button.click();
+      resolve();
+    }
+
+    Services.obs.addObserver(onDialogShown, "tabmodal-dialog-loaded");
+  });
+}
+
+SpecialPowers.pushPrefEnv(
+  {"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
+/**
+ * Test navigation from a content page to a chrome page. Also check that only
+ * one beforeunload event is fired.
+ */
+add_task(function* () {
+  let beforeUnloadCount = 0;
+  messageManager.addMessageListener("Test:OnBeforeUnloadReceived", function() {
+    beforeUnloadCount++;
+  });
+
+  // Open a content page.
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+  let browser = tab.linkedBrowser;
+
+  ok(browser.isRemoteBrowser, "Browser should be remote.");
+  browser.messageManager.loadFrameScript(
+    "data:,(" + frameScript.toString() + ")();", true);
+
+  // Navigate to a chrome page.
+  let dialogShown1 = awaitAndCloseBeforeUnloadDialog(false);
+  yield BrowserTestUtils.loadURI(browser, "about:support");
+  yield Promise.all([
+    dialogShown1,
+    BrowserTestUtils.browserLoaded(browser)
+  ]);
+  is(beforeUnloadCount, 1, "Should have received one beforeunload event.");
+  ok(!browser.isRemoteBrowser, "Browser should not be remote.");
+
+  // Go back to content page.
+  ok(gBrowser.webNavigation.canGoBack, "Should be able to go back.");
+  gBrowser.goBack();
+  yield BrowserTestUtils.browserLoaded(browser);
+  browser.messageManager.loadFrameScript(
+    "data:,(" + frameScript.toString() + ")();", true);
+
+  // Test that going forward triggers beforeunload prompt as well.
+  ok(gBrowser.webNavigation.canGoForward, "Should be able to go forward.");
+  let dialogShown2 = awaitAndCloseBeforeUnloadDialog(false);
+  gBrowser.goForward();
+  yield Promise.all([
+    dialogShown2,
+    BrowserTestUtils.browserLoaded(browser)
+  ]);
+  is(beforeUnloadCount, 2, "Should have received two beforeunload events.");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
+
+/**
+ * Test navigation from a chrome page to a content page. Also check that only
+ * one beforeunload event is fired.
+ */
+add_task(function* () {
+  let beforeUnloadCount = 0;
+  messageManager.addMessageListener("Test:OnBeforeUnloadReceived", function() {
+    beforeUnloadCount++;
+  });
+
+  // Open a chrome page.
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
+                                                        "about:support");
+  let browser = tab.linkedBrowser;
+
+  ok(!browser.isRemoteBrowser, "Browser should not be remote.");
+  yield ContentTask.spawn(browser, null, function* () {
+    content.window.addEventListener("beforeunload", function (event) {
+      sendAsyncMessage("Test:OnBeforeUnloadReceived");
+      var str = "Leaving?";
+      event.returnValue = str;
+      return str;
+    }, true);
+  });
+
+  // Navigate to a content page.
+  let dialogShown1 = awaitAndCloseBeforeUnloadDialog(false);
+  yield BrowserTestUtils.loadURI(browser, TEST_URL);
+  yield Promise.all([
+    dialogShown1,
+    BrowserTestUtils.browserLoaded(browser)
+  ]);
+  is(beforeUnloadCount, 1, "Should have received one beforeunload event.");
+  ok(browser.isRemoteBrowser, "Browser should be remote.");
+
+  // Go back to chrome page.
+  ok(gBrowser.webNavigation.canGoBack, "Should be able to go back.");
+  gBrowser.goBack();
+  yield BrowserTestUtils.browserLoaded(browser);
+  yield ContentTask.spawn(browser, null, function* () {
+    content.window.addEventListener("beforeunload", function (event) {
+      sendAsyncMessage("Test:OnBeforeUnloadReceived");
+      var str = "Leaving?";
+      event.returnValue = str;
+      return str;
+    }, true);
+  });
+
+  // Test that going forward triggers beforeunload prompt as well.
+  ok(gBrowser.webNavigation.canGoForward, "Should be able to go forward.");
+  let dialogShown2 = awaitAndCloseBeforeUnloadDialog(false);
+  gBrowser.goForward();
+  yield Promise.all([
+    dialogShown2,
+    BrowserTestUtils.browserLoaded(browser)
+  ]);
+  is(beforeUnloadCount, 2, "Should have received two beforeunload events.");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/dom/webidl/Node.webidl
+++ b/dom/webidl/Node.webidl
@@ -54,17 +54,17 @@ interface Node : EventTarget {
   readonly attribute Node? lastChild;
   [Pure]
   readonly attribute Node? previousSibling;
   [Pure]
   readonly attribute Node? nextSibling;
 
   [SetterThrows, Pure]
            attribute DOMString? nodeValue;
-  [Throws, Pure]
+  [SetterThrows, GetterCanOOM, Pure]
            attribute DOMString? textContent;
   [Throws]
   Node insertBefore(Node node, Node? child);
   [Throws]
   Node appendChild(Node node);
   [Throws]
   Node replaceChild(Node node, Node child);
   [Throws]
--- a/editor/libeditor/tests/test_bug1330796.html
+++ b/editor/libeditor/tests/test_bug1330796.html
@@ -70,18 +70,18 @@ SimpleTest.waitForFocus(function() {
 
   for (i = 0; i < tests.length; i++) {
     theEdit.innerHTML = tests[i][0];
     theEdit.focus();
     var theText = theEdit.firstChild.firstChild;
     // Position set at the beginning , middle and end of the text.
     sel.collapse(theText, tests[i][1]);
 
-    synthesizeKey("KEY_Enter", {});
-    synthesizeKey("x", {});
+    synthesizeKey("KEY_Enter", { code: "Enter" });
+    synthesizeKey("x", { code: "KeyX" });
     is(theEdit.innerHTML, tests[i][2], "unexpected HTML for test " + i.toString());
   }
 
   SimpleTest.finish();
 
 });
 
 function makeMailEditor() {
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -709,16 +709,29 @@ struct ParamTraits<mozilla::layers::Laye
   static void Write(Message* msg, const paramType& param) {
     WriteParam(msg, param.mHandle);
   }
   static bool Read(const Message* msg, PickleIterator* iter, paramType* result) {
     return ReadParam(msg, iter, &result->mHandle);
   }
 };
 
+template<>
+struct ParamTraits<mozilla::layers::CompositableHandle>
+{
+  typedef mozilla::layers::CompositableHandle paramType;
+
+  static void Write(Message* msg, const paramType& param) {
+    WriteParam(msg, param.mHandle);
+  }
+  static bool Read(const Message* msg, PickleIterator* iter, paramType* result) {
+    return ReadParam(msg, iter, &result->mHandle);
+  }
+};
+
 // Helper class for reading bitfields.
 // If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>.
 template <typename ParamType>
 struct BitfieldHelper
 {
   // We need this helper because we can't get the address of a bitfield to
   // pass directly to ReadParam. So instead we read it into a temporary bool
   // and set the bitfield using a setter function
--- a/gfx/layers/AsyncCanvasRenderer.cpp
+++ b/gfx/layers/AsyncCanvasRenderer.cpp
@@ -24,17 +24,16 @@ namespace layers {
 
 AsyncCanvasRenderer::AsyncCanvasRenderer()
   : mHTMLCanvasElement(nullptr)
   , mContext(nullptr)
   , mGLContext(nullptr)
   , mIsAlphaPremultiplied(true)
   , mWidth(0)
   , mHeight(0)
-  , mCanvasClientAsyncID(0)
   , mCanvasClient(nullptr)
   , mMutex("AsyncCanvasRenderer::mMutex")
 {
   MOZ_COUNT_CTOR(AsyncCanvasRenderer);
 }
 
 AsyncCanvasRenderer::~AsyncCanvasRenderer()
 {
@@ -111,19 +110,19 @@ AsyncCanvasRenderer::NotifyElementAboutI
   }
 }
 
 void
 AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
 {
   mCanvasClient = aClient;
   if (aClient) {
-    mCanvasClientAsyncID = aClient->GetAsyncID();
+    mCanvasClientAsyncHandle = aClient->GetAsyncHandle();
   } else {
-    mCanvasClientAsyncID = 0;
+    mCanvasClientAsyncHandle = CompositableHandle();
   }
 }
 
 void
 AsyncCanvasRenderer::SetActiveThread()
 {
   MutexAutoLock lock(mMutex);
   mActiveThread = NS_GetCurrentThread();
--- a/gfx/layers/AsyncCanvasRenderer.h
+++ b/gfx/layers/AsyncCanvasRenderer.h
@@ -101,19 +101,19 @@ public:
                  const char16_t *aEncoderOptions,
                  nsIInputStream **aStream);
 
   gfx::IntSize GetSize() const
   {
     return gfx::IntSize(mWidth, mHeight);
   }
 
-  uint64_t GetCanvasClientAsyncID() const
+  CompositableHandle GetCanvasClientAsyncHandle() const
   {
-    return mCanvasClientAsyncID;
+    return mCanvasClientAsyncHandle;
   }
 
   CanvasClient* GetCanvasClient() const
   {
     return mCanvasClient;
   }
 
   already_AddRefed<nsIThread> GetActiveThread();
@@ -135,17 +135,17 @@ private:
 
   // Readback current WebGL's content and return it as DataSourceSurface.
   already_AddRefed<gfx::DataSourceSurface> UpdateTarget();
 
   bool mIsAlphaPremultiplied;
 
   uint32_t mWidth;
   uint32_t mHeight;
-  uint64_t mCanvasClientAsyncID;
+  CompositableHandle mCanvasClientAsyncHandle;
 
   // The lifetime of this pointer is controlled by OffscreenCanvas
   // Can be accessed in active thread and ImageBridge thread.
   // But we never accessed it at the same time on both thread. So no
   // need to protect this member.
   CanvasClient* mCanvasClient;
 
   // When backend is LAYER_BASIC and SharedSurface type is Basic.
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -131,59 +131,57 @@ ImageContainer::EnsureImageClient(bool a
   if (!aCreate && (!mImageClient || mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen())) {
     return;
   }
 
   RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
   if (imageBridge) {
     mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this);
     if (mImageClient) {
-      mAsyncContainerID = mImageClient->GetAsyncID();
+      mAsyncContainerHandle = mImageClient->GetAsyncHandle();
       mNotifyCompositeListener = new ImageContainerListener(this);
     }
   }
 }
 
 ImageContainer::ImageContainer(Mode flag)
 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
   mCurrentProducerID(-1)
 {
   if (flag == ASYNCHRONOUS) {
     EnsureImageClient(true);
-  } else {
-    mAsyncContainerID = sInvalidAsyncContainerId;
   }
 }
 
-ImageContainer::ImageContainer(uint64_t aAsyncContainerID)
+ImageContainer::ImageContainer(const CompositableHandle& aHandle)
   : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
   mImageFactory(nullptr),
   mRecycleBin(nullptr),
-  mAsyncContainerID(aAsyncContainerID),
+  mAsyncContainerHandle(aHandle),
   mCurrentProducerID(-1)
 {
-  MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
+  MOZ_ASSERT(mAsyncContainerHandle);
 }
 
 ImageContainer::~ImageContainer()
 {
   if (mNotifyCompositeListener) {
     mNotifyCompositeListener->ClearImageContainer();
   }
-  if (mAsyncContainerID) {
+  if (mAsyncContainerHandle) {
     if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
-      imageBridge->ForgetImageContainer(mAsyncContainerID);
+      imageBridge->ForgetImageContainer(mAsyncContainerHandle);
     }
   }
 }
 
 RefPtr<PlanarYCbCrImage>
 ImageContainer::CreatePlanarYCbCrImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
@@ -339,24 +337,24 @@ ImageContainer::SetCurrentImagesInTransa
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
 
   SetCurrentImageInternal(aImages);
 }
 
 bool ImageContainer::IsAsync() const
 {
-  return mAsyncContainerID != sInvalidAsyncContainerId;
+  return !!mAsyncContainerHandle;
 }
 
-uint64_t ImageContainer::GetAsyncContainerID()
+CompositableHandle ImageContainer::GetAsyncContainerHandle()
 {
   NS_ASSERTION(IsAsync(),"Shared image ID is only relevant to async ImageContainers");
   EnsureImageClient(false);
-  return mAsyncContainerID;
+  return mAsyncContainerHandle;
 }
 
 bool
 ImageContainer::HasCurrentImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   return !mCurrentImages.IsEmpty();
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -380,17 +380,17 @@ public:
 
   explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
 
   /**
    * Create ImageContainer just to hold another ASYNCHRONOUS ImageContainer's
    * async container ID.
    * @param aAsyncContainerID async container ID for which we are a proxy
    */
-  explicit ImageContainer(uint64_t aAsyncContainerID);
+  explicit ImageContainer(const CompositableHandle& aHandle);
 
   typedef uint32_t FrameID;
   typedef uint32_t ProducerID;
 
   RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
 
   // Factory methods for shared image types.
   RefPtr<SharedRGBImage> CreateSharedRGBImage();
@@ -481,17 +481,17 @@ public:
   /**
    * If this ImageContainer uses ImageBridge, returns the ID associated to
    * this container, for use in the ImageBridge protocol.
    * Returns 0 if this ImageContainer does not use ImageBridge. Note that
    * 0 is always an invalid ID for asynchronous image containers.
    *
    * Can be called from any thread.
    */
-  uint64_t GetAsyncContainerID();
+  CompositableHandle GetAsyncContainerHandle();
 
   /**
    * Returns if the container currently has an image.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
   bool HasCurrentImage();
 
@@ -644,17 +644,17 @@ private:
   // sucessfully created with ENABLE_ASYNC, or points to null otherwise.
   // 'unsuccessful' in this case only means that the ImageClient could not
   // be created, most likely because off-main-thread compositing is not enabled.
   // In this case the ImageContainer is perfectly usable, but it will forward
   // frames to the compositor through transactions in the main thread rather than
   // asynchronusly using the ImageBridge IPDL protocol.
   RefPtr<ImageClient> mImageClient;
 
-  uint64_t mAsyncContainerID;
+  CompositableHandle mAsyncContainerHandle;
 
   nsTArray<FrameID> mFrameIDsNotYetComposited;
   // ProducerID for last current image(s), including the frames in
   // mFrameIDsNotYetComposited
   ProducerID mCurrentProducerID;
 
   RefPtr<ImageContainerListener> mNotifyCompositeListener;
 
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -274,12 +274,42 @@ public:
   }
   uint64_t Value() const {
     return mHandle;
   }
 private:
   uint64_t mHandle;
 };
 
+// This is used to communicate Compositables across IPC channels. The Handle is valid
+// for layers in the same PLayerTransaction or PImageBridge. Handles are created by
+// ClientLayerManager or ImageBridgeChild, and are cached in the parent side on first
+// use.
+class CompositableHandle
+{
+  friend struct IPC::ParamTraits<mozilla::layers::CompositableHandle>;
+public:
+  CompositableHandle() : mHandle(0)
+  {}
+  CompositableHandle(const CompositableHandle& aOther) : mHandle(aOther.mHandle)
+  {}
+  explicit CompositableHandle(uint64_t aHandle) : mHandle(aHandle)
+  {}
+  bool IsValid() const {
+    return mHandle != 0;
+  }
+  explicit operator bool() const {
+    return IsValid();
+  }
+  bool operator ==(const CompositableHandle& aOther) const {
+    return mHandle == aOther.mHandle;
+  }
+  uint64_t Value() const {
+    return mHandle;
+  }
+private:
+  uint64_t mHandle;
+};
+
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_LAYERSTYPES_H */
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -50,24 +50,24 @@ CanvasClient::CreateCanvasClient(CanvasC
 void
 CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer)
 {
   if (!GetForwarder() || !mLayer || !aRenderer ||
       !aRenderer->GetCanvasClient()) {
     return;
   }
 
-  uint64_t asyncID = aRenderer->GetCanvasClientAsyncID();
-  if (asyncID == 0 || mAsyncID == asyncID) {
+  CompositableHandle asyncID = aRenderer->GetCanvasClientAsyncHandle();
+  if (!asyncID || mAsyncHandle == asyncID) {
     return;
   }
 
   static_cast<ShadowLayerForwarder*>(GetForwarder())
     ->AttachAsyncCompositable(asyncID, mLayer);
-  mAsyncID = asyncID;
+  mAsyncHandle = asyncID;
 }
 
 void
 CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
 {
   MOZ_ASSERT(aTexture);
 
   if (!aTexture->IsSharedWithCompositor()) {
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -179,17 +179,16 @@ public:
  * only forward its AsyncID here
  */
 class CanvasClientBridge final : public CanvasClient
 {
 public:
   CanvasClientBridge(CompositableForwarder* aLayerForwarder,
                      TextureFlags aFlags)
     : CanvasClient(aLayerForwarder, aFlags)
-    , mAsyncID(0)
     , mLayer(nullptr)
   {
   }
 
   TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::IMAGE);
   }
@@ -201,16 +200,16 @@ public:
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
 
   void SetLayer(ShadowableLayer* aLayer)
   {
     mLayer = aLayer;
   }
 
 protected:
-  uint64_t mAsyncID;
+  CompositableHandle mAsyncHandle;
   ShadowableLayer* mLayer;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -8,17 +8,16 @@
 #include "gfxPrefs.h"                   // for gfxPrefs::LayersTile...
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Hal.h"
 #include "mozilla/dom/ScreenOrientation.h"  // for ScreenOrientation
 #include "mozilla/dom/TabChild.h"       // for TabChild
 #include "mozilla/hal_sandbox/PHal.h"   // for ScreenConfiguration
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
-#include "mozilla/layers/ContentClient.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "ClientReadbackLayer.h"        // for ClientReadbackLayer
 #include "nsAString.h"
@@ -655,17 +654,18 @@ ClientLayerManager::StopFrameTimeRecordi
 void
 ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
 {
   TimeStamp start = TimeStamp::Now();
 
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
-    if (mForwarder->GetSyncObject()) {
+    if (mForwarder->GetSyncObject() &&
+        mForwarder->GetSyncObject()->IsSyncObjectValid()) {
       mForwarder->GetSyncObject()->FinalizeFrame();
     }
   }
 
   mPhase = PHASE_FORWARD;
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
   TimeStamp transactionStart;
@@ -675,45 +675,22 @@ ClientLayerManager::ForwardTransaction(b
     transactionStart = mTransactionStart;
   }
 
   if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
     mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
   }
 
   // forward this transaction's changeset to our LayerManagerComposite
-  bool sent;
-  AutoTArray<EditReply, 10> replies;
-  if (mForwarder->EndTransaction(&replies, mRegionToClear,
-        mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
-        mIsRepeatTransaction, transactionStart, &sent)) {
-    for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
-      const EditReply& reply = replies[i];
-
-      switch (reply.type()) {
-      case EditReply::TOpContentBufferSwap: {
-        MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
-
-        const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
-
-        RefPtr<CompositableClient> compositable =
-          CompositableClient::FromIPDLActor(obs.compositableChild());
-        ContentClientRemote* contentClient =
-          static_cast<ContentClientRemote*>(compositable.get());
-        MOZ_ASSERT(contentClient);
-
-        contentClient->SwapBuffers(obs.frontUpdatedRegion());
-
-        break;
-      }
-      default:
-        MOZ_CRASH("not reached");
-      }
-    }
-
+  bool sent = false;
+  bool ok = mForwarder->EndTransaction(
+    mRegionToClear, mLatestTransactionId, aScheduleComposite,
+    mPaintSequenceNumber, mIsRepeatTransaction, transactionStart,
+    &sent);
+  if (ok) {
     if (sent) {
       mNeedsComposite = false;
     }
   } else if (HasShadowManager()) {
     NS_WARNING("failed to forward Layers transaction");
   }
 
   if (!sent) {
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -94,17 +94,17 @@ ClientPaintedLayer::PaintThebes()
   }
 
   if (didUpdate) {
     Mutated();
 
     mValidRegion.Or(mValidRegion, state.mRegionToDraw);
 
     ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
-    MOZ_ASSERT(contentClientRemote->GetIPDLActor());
+    MOZ_ASSERT(contentClientRemote->GetIPCHandle());
 
     // Hold(this) ensures this layer is kept alive through the current transaction
     // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
     // so deleting this Hold for whatever reason will break things.
     ClientManager()->Hold(this);
     contentClientRemote->Updated(state.mRegionToDraw,
                                  mVisibleRegion.ToUnknownRegion(),
                                  state.mDidSelfCopy);
deleted file mode 100644
--- a/gfx/layers/client/CompositableChild.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; 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/. */
-
-#include "CompositableChild.h"
-#include "CompositableClient.h"
-
-namespace mozilla {
-namespace layers {
-
-/* static */ PCompositableChild*
-CompositableChild::CreateActor()
-{
-  CompositableChild* child = new CompositableChild();
-  child->AddRef();
-  return child;
-}
-
-/* static */ void
-CompositableChild::DestroyActor(PCompositableChild* aChild)
-{
-  static_cast<CompositableChild*>(aChild)->Release();
-}
-
-CompositableChild::CompositableChild()
- : mCompositableClient(nullptr),
-   mCanSend(true)
-{
-}
-
-CompositableChild::~CompositableChild()
-{
-}
-
-bool
-CompositableChild::IsConnected() const
-{
-  return mCompositableClient && mCanSend;
-}
-
-void
-CompositableChild::Init(CompositableClient* aCompositable)
-{
-  mCompositableClient = aCompositable;
-}
-
-void
-CompositableChild::RevokeCompositableClient()
-{
-  mCompositableClient = nullptr;
-}
-
-RefPtr<CompositableClient>
-CompositableChild::GetCompositableClient()
-{
-  return mCompositableClient;
-}
-
-void
-CompositableChild::ActorDestroy(ActorDestroyReason)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mCanSend = false;
-
-  if (mCompositableClient) {
-    mCompositableClient->mCompositableChild = nullptr;
-    mCompositableClient = nullptr;
-  }
-}
-
-/* static */ PCompositableChild*
-AsyncCompositableChild::CreateActor(uint64_t aAsyncID)
-{
-  AsyncCompositableChild* child = new AsyncCompositableChild(aAsyncID);
-  child->AddRef();
-  return child;
-}
-
-AsyncCompositableChild::AsyncCompositableChild(uint64_t aAsyncID)
- : mLock("AsyncCompositableChild.mLock"),
-   mAsyncID(aAsyncID)
-{
-}
-
-AsyncCompositableChild::~AsyncCompositableChild()
-{
-}
-
-void
-AsyncCompositableChild::ActorDestroy(ActorDestroyReason)
-{
-  mCanSend = false;
-
-  // We do not revoke CompositableClient::mCompositableChild here, since that
-  // could race with the main thread.
-  RevokeCompositableClient();
-}
-
-void
-AsyncCompositableChild::RevokeCompositableClient()
-{
-  MutexAutoLock lock(mLock);
-  mCompositableClient = nullptr;
-}
-
-RefPtr<CompositableClient>
-AsyncCompositableChild::GetCompositableClient()
-{
-  MutexAutoLock lock(mLock);
-  return CompositableChild::GetCompositableClient();
-}
-
-} // namespace layers
-} // namespace mozilla
deleted file mode 100644
--- a/gfx/layers/client/CompositableChild.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* -*- Mode: C++; tab-width: 20; 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/. */
-
-#ifndef mozilla_gfx_layers_client_CompositableChild_h
-#define mozilla_gfx_layers_client_CompositableChild_h
-
-#include <stdint.h>
-#include "IPDLActor.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/layers/PCompositableChild.h"
-
-namespace mozilla {
-namespace layers {
-
-class CompositableClient;
-class AsyncCompositableChild;
-
-/**
- * IPDL actor used by CompositableClient to match with its corresponding
- * CompositableHost on the compositor side.
- *
- * CompositableChild is owned by a CompositableClient.
- */
-class CompositableChild : public PCompositableChild
-{
-public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild)
-
-  static PCompositableChild* CreateActor();
-  static void DestroyActor(PCompositableChild* aChild);
-
-  void Init(CompositableClient* aCompositable);
-  virtual void RevokeCompositableClient();
-
-  virtual void ActorDestroy(ActorDestroyReason) override;
-
-  virtual RefPtr<CompositableClient> GetCompositableClient();
-
-  virtual AsyncCompositableChild* AsAsyncCompositableChild() {
-    return nullptr;
-  }
-
-  // These should only be called on the IPDL thread.
-  bool IsConnected() const;
-  bool CanSend() const {
-    return mCanSend;
-  }
-
-protected:
-  CompositableChild();
-  virtual ~CompositableChild();
-
-protected:
-  CompositableClient* mCompositableClient;
-  bool mCanSend;
-};
-
-// This CompositableChild can be used off the main thread.
-class AsyncCompositableChild final : public CompositableChild
-{
-public:
-  static PCompositableChild* CreateActor(uint64_t aAsyncID);
-
-  void RevokeCompositableClient() override;
-  RefPtr<CompositableClient> GetCompositableClient() override;
-
-  void ActorDestroy(ActorDestroyReason) override;
-
-  AsyncCompositableChild* AsAsyncCompositableChild() override {
-    return this;
-  }
-
-  uint64_t GetAsyncID() const {
-    return mAsyncID;
-  }
-
-protected:
-  explicit AsyncCompositableChild(uint64_t aAsyncID);
-  ~AsyncCompositableChild() override;
-
-private:
-  Mutex mLock;
-  uint64_t mAsyncID;
-};
-
-} // namespace layers
-} // namespace mozilla
-
-#endif // mozilla_gfx_layers_client_CompositableChild_h
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -1,135 +1,106 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 #include "mozilla/layers/CompositableClient.h"
 #include <stdint.h>                     // for uint64_t, uint32_t
 #include "gfxPlatform.h"                // for gfxPlatform
-#include "mozilla/layers/CompositableChild.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
 #include "mozilla/layers/TextureClientOGL.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
-#include "mozilla/layers/PCompositableChild.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"         // for gfxWindowsPlatform
 #include "mozilla/layers/TextureD3D11.h"
 #include "mozilla/layers/TextureD3D9.h"
 #endif
 #include "gfxUtils.h"
 #include "IPDLActor.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
-CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
+CompositableClient::InitIPDL(const CompositableHandle& aHandle)
 {
-  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aHandle);
 
   mForwarder->AssertInForwarderThread();
 
-  mAsyncID = aAsyncID;
-  mCompositableChild = static_cast<CompositableChild*>(aActor);
-  mCompositableChild->Init(this);
-}
-
-/* static */ RefPtr<CompositableClient>
-CompositableClient::FromIPDLActor(PCompositableChild* aActor)
-{
-  MOZ_ASSERT(aActor);
-
-  RefPtr<CompositableClient> client = static_cast<CompositableChild*>(aActor)->GetCompositableClient();
-  if (!client) {
-    return nullptr;
-  }
-
-  client->mForwarder->AssertInForwarderThread();
-  return client;
+  mHandle = aHandle;
+  mIsAsync = !NS_IsMainThread();
 }
 
 CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
                                        TextureFlags aTextureFlags)
 : mForwarder(aForwarder)
 , mTextureFlags(aTextureFlags)
-, mAsyncID(0)
+, mIsAsync(false)
 {
 }
 
 CompositableClient::~CompositableClient()
 {
   Destroy();
 }
 
 LayersBackend
 CompositableClient::GetCompositorBackendType() const
 {
   return mForwarder->GetCompositorBackendType();
 }
 
-PCompositableChild*
-CompositableClient::GetIPDLActor() const
-{
-  return mCompositableChild;
-}
-
 bool
 CompositableClient::Connect(ImageContainer* aImageContainer)
 {
-  MOZ_ASSERT(!mCompositableChild);
-  if (!GetForwarder() || GetIPDLActor()) {
+  MOZ_ASSERT(!mHandle);
+  if (!GetForwarder() || mHandle) {
     return false;
   }
 
   GetForwarder()->AssertInForwarderThread();
   GetForwarder()->Connect(this, aImageContainer);
   return true;
 }
 
 bool
 CompositableClient::IsConnected() const
 {
   // CanSend() is only reliable in the same thread as the IPDL channel.
   mForwarder->AssertInForwarderThread();
-  return mCompositableChild && mCompositableChild->IsConnected();
+  return !!mHandle;
 }
 
 void
 CompositableClient::Destroy()
 {
-  if (!mCompositableChild) {
-    return;
-  }
-
   if (mTextureClientRecycler) {
     mTextureClientRecycler->Destroy();
   }
 
-  // Take away our IPDL's actor reference back to us.
-  mCompositableChild->RevokeCompositableClient();
-
-  // Schedule the IPDL actor to be destroyed on the forwarder's thread.
-  mForwarder->Destroy(mCompositableChild);
-  mCompositableChild = nullptr;
+  if (mHandle) {
+    mForwarder->ReleaseCompositable(mHandle);
+    mHandle = CompositableHandle();
+  }
 }
 
-uint64_t
-CompositableClient::GetAsyncID() const
+CompositableHandle
+CompositableClient::GetAsyncHandle() const
 {
-  if (mCompositableChild) {
-    return mAsyncID;
+  if (mIsAsync) {
+    return mHandle;
   }
-  return 0; // zero is always an invalid async ID
+  return CompositableHandle();
 }
 
 already_AddRefed<TextureClient>
 CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
                                               gfx::IntSize aSize,
                                               gfx::BackendType aMoz2DBackend,
                                               TextureFlags aTextureFlags)
 {
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -22,16 +22,17 @@ namespace layers {
 
 class CompositableClient;
 class ImageBridgeChild;
 class ImageContainer;
 class CompositableForwarder;
 class CompositableChild;
 class PCompositableChild;
 class TextureClientRecycleAllocator;
+class ContentClientRemote;
 
 /**
  * CompositableClient manages the texture-specific logic for composite layers,
  * independently of the layer. It is the content side of a CompositableClient/
  * CompositableHost pair.
  *
  * CompositableClient's purpose is to send texture data to the compositor side
  * along with any extra information about how the texture is to be composited.
@@ -108,32 +109,37 @@ public:
    * Establishes the connection with compositor side through IPDL
    */
   virtual bool Connect(ImageContainer* aImageContainer = nullptr);
 
   void Destroy();
 
   bool IsConnected() const;
 
-  PCompositableChild* GetIPDLActor() const;
-
   CompositableForwarder* GetForwarder() const
   {
     return mForwarder;
   }
 
   /**
    * This identifier is what lets us attach async compositables with a shadow
    * layer. It is not used if the compositable is used with the regular shadow
    * layer forwarder.
    *
-   * If this returns zero, it means the compositable is not async (it is used
+   * If this returns empty, it means the compositable is not async (it is used
    * on the main thread).
    */
-  uint64_t GetAsyncID() const;
+  CompositableHandle GetAsyncHandle() const;
+
+  /**
+   * Handle for IPDL communication.
+   */
+  CompositableHandle GetIPCHandle() const {
+    return mHandle;
+  }
 
   /**
    * Tells the Compositor to create a TextureHost for this TextureClient.
    */
   virtual bool AddTextureClient(TextureClient* aClient);
 
   /**
    * A hook for the when the Compositable is detached from it's layer.
@@ -155,38 +161,38 @@ public:
   /**
    * Should be called when deataching a TextureClient from a Compositable, because
    * some platforms need to do some extra book keeping when this happens.
    *
    * See AutoRemoveTexture to automatically invoke this at the end of a scope.
    */
   virtual void RemoveTexture(TextureClient* aTexture);
 
-  static RefPtr<CompositableClient> FromIPDLActor(PCompositableChild* aActor);
+  virtual ContentClientRemote* AsContentClientRemote() { return nullptr; }
 
-  void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
+  void InitIPDL(const CompositableHandle& aHandle);
 
   TextureFlags GetTextureFlags() const { return mTextureFlags; }
 
   TextureClientRecycleAllocator* GetTextureClientRecycler();
 
   bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
 
   static void DumpTextureClient(std::stringstream& aStream,
                                 TextureClient* aTexture,
                                 TextureDumpMode aCompress);
 protected:
-  RefPtr<CompositableChild> mCompositableChild;
   RefPtr<CompositableForwarder> mForwarder;
   // Some layers may want to enforce some flags to all their textures
   // (like disallowing tiling)
   TextureFlags mTextureFlags;
   RefPtr<TextureClientRecycleAllocator> mTextureClientRecycler;
 
-  uint64_t mAsyncID;
+  CompositableHandle mHandle;
+  bool mIsAsync;
 
   friend class CompositableChild;
 };
 
 /**
  * Helper to call RemoveTexture at the end of a scope.
  */
 struct AutoRemoveTexture
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -114,16 +114,20 @@ class ContentClientRemote : public Conte
 public:
   explicit ContentClientRemote(CompositableForwarder* aForwarder)
     : ContentClient(aForwarder)
   {}
 
   virtual void Updated(const nsIntRegion& aRegionToDraw,
                        const nsIntRegion& aVisibleRegion,
                        bool aDidSelfCopy) = 0;
+
+  ContentClientRemote* AsContentClientRemote() override {
+    return this;
+  }
 };
 
 // thin wrapper around RotatedContentBuffer, for on-mtc
 class ContentClientBasic final : public ContentClient
                                , protected RotatedContentBuffer
 {
 public:
   explicit ContentClientBasic(gfx::BackendType aBackend);
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -273,28 +273,27 @@ ImageClient::ImageClient(CompositableFor
 , mLayer(nullptr)
 , mType(aType)
 , mLastUpdateGenerationCounter(0)
 {}
 
 ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
                                      TextureFlags aFlags)
 : ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
-, mAsyncContainerID(0)
 {
 }
 
 bool
 ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
 {
   if (!GetForwarder() || !mLayer) {
     return false;
   }
-  if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
+  if (mAsyncContainerHandle == aContainer->GetAsyncContainerHandle()) {
     return true;
   }
-  mAsyncContainerID = aContainer->GetAsyncContainerID();
-  static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
+  mAsyncContainerHandle = aContainer->GetAsyncContainerHandle();
+  static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerHandle, mLayer);
   return true;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -124,15 +124,15 @@ public:
   virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(mType);
   }
 
 protected:
-  uint64_t mAsyncContainerID;
+  CompositableHandle mAsyncContainerHandle;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -106,16 +106,17 @@ public:
   static already_AddRefed<SyncObject> CreateSyncObject(SyncHandle aHandle);
 
   enum class SyncType {
     D3D11,
   };
 
   virtual SyncType GetSyncType() = 0;
   virtual void FinalizeFrame() = 0;
+  virtual bool IsSyncObjectValid() = 0;
 
 protected:
   SyncObject() { }
 };
 
 /**
  * This class may be used to asynchronously receive an update when the content
  * drawn to this texture client is available for reading in CPU memory. This
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -12,59 +12,26 @@
 #include "ImageHost.h"                  // for ImageHostBuffered, etc
 #include "TiledContentHost.h"           // for TiledContentHost
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "gfxPlatform.h"                // for gfxPlatform
-#include "mozilla/layers/PCompositableParent.h"
 #include "IPDLActor.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 class Compositor;
 
-/**
- * IPDL actor used by CompositableHost to match with its corresponding
- * CompositableClient on the content side.
- *
- * CompositableParent is owned by the IPDL system. It's deletion is triggered
- * by either the CompositableChild's deletion, or by the IPDL communication
- * going down.
- */
-class CompositableParent : public ParentActor<PCompositableParent>
-{
-public:
-  CompositableParent(CompositableParentManager* aMgr, const TextureInfo& aTextureInfo)
-  {
-    MOZ_COUNT_CTOR(CompositableParent);
-    mHost = CompositableHost::Create(aTextureInfo);
-  }
-
-  ~CompositableParent()
-  {
-    MOZ_COUNT_DTOR(CompositableParent);
-  }
-
-  virtual void Destroy() override
-  {
-    if (mHost) {
-      mHost->Detach(nullptr, CompositableHost::FORCE_DETACH);
-    }
-  }
-
-  RefPtr<CompositableHost> mHost;
-};
-
 CompositableHost::CompositableHost(const TextureInfo& aTextureInfo)
   : mTextureInfo(aTextureInfo)
   , mCompositorID(0)
   , mCompositor(nullptr)
   , mLayer(nullptr)
   , mFlashCounter(0)
   , mAttached(false)
   , mKeepAttached(false)
@@ -72,37 +39,16 @@ CompositableHost::CompositableHost(const
   MOZ_COUNT_CTOR(CompositableHost);
 }
 
 CompositableHost::~CompositableHost()
 {
   MOZ_COUNT_DTOR(CompositableHost);
 }
 
-PCompositableParent*
-CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
-                                  const TextureInfo& aTextureInfo)
-{
-  return new CompositableParent(aMgr, aTextureInfo);
-}
-
-bool
-CompositableHost::DestroyIPDLActor(PCompositableParent* aActor)
-{
-  delete aActor;
-  return true;
-}
-
-CompositableHost*
-CompositableHost::FromIPDLActor(PCompositableParent* aActor)
-{
-  MOZ_ASSERT(aActor);
-  return static_cast<CompositableParent*>(aActor)->mHost;
-}
-
 void
 CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
 {
   if (GetCompositor()) {
     for (auto& texture : aTextures) {
       texture.mTexture->SetCompositor(GetCompositor());
     }
   }
@@ -204,16 +150,10 @@ CompositableHost::DumpTextureHost(std::s
   }
   RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
   if (!dSurf) {
     return;
   }
   aStream << gfxUtils::GetAsDataURI(dSurf).get();
 }
 
-void
-CompositableHost::ReceivedDestroy(PCompositableParent* aActor)
-{
-  static_cast<CompositableParent*>(aActor)->RecvDestroy();
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -38,31 +38,29 @@ class DataSourceSurface;
 namespace layers {
 
 class Layer;
 class LayerComposite;
 class Compositor;
 class ThebesBufferData;
 class TiledContentHost;
 class CompositableParentManager;
-class PCompositableParent;
 struct EffectChain;
 
 struct AsyncCompositableRef
 {
   AsyncCompositableRef()
-   : mProcessId(mozilla::ipc::kInvalidProcessId),
-     mAsyncId(0)
+   : mProcessId(mozilla::ipc::kInvalidProcessId)
   {}
-  AsyncCompositableRef(base::ProcessId aProcessId, uint64_t aAsyncId)
-   : mProcessId(aProcessId), mAsyncId(aAsyncId)
+  AsyncCompositableRef(base::ProcessId aProcessId, const CompositableHandle& aHandle)
+   : mProcessId(aProcessId), mHandle(aHandle)
   {}
-  explicit operator bool() const { return !!mAsyncId; }
+  explicit operator bool() const { return !!mHandle; }
   base::ProcessId mProcessId;
-  uint64_t mAsyncId;
+  CompositableHandle mHandle;
 };
 
 /**
  * The compositor-side counterpart to CompositableClient. Responsible for
  * updating textures and data about textures from IPC and how textures are
  * composited (tiling, double buffering, etc.).
  *
  * Update (for images/canvases) and UpdateThebes (for Thebes) are called during
@@ -75,17 +73,17 @@ struct AsyncCompositableRef
  * rendering.
  */
 class CompositableHost
 {
 protected:
   virtual ~CompositableHost();
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(CompositableHost)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableHost)
   explicit CompositableHost(const TextureInfo& aTextureInfo);
 
   static already_AddRefed<CompositableHost> Create(const TextureInfo& aTextureInfo);
 
   virtual CompositableType GetType() = 0;
 
   // If base class overrides, it should still call the parent implementation
   virtual void SetCompositor(Compositor* aCompositor);
@@ -183,19 +181,16 @@ public:
         aFlags & FORCE_DETACH) {
       SetLayer(nullptr);
       mAttached = false;
       mKeepAttached = false;
     }
   }
   bool IsAttached() { return mAttached; }
 
-  static void
-  ReceivedDestroy(PCompositableParent* aActor);
-
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) { }
   static void DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture);
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() { return nullptr; }
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
@@ -216,24 +211,16 @@ public:
   virtual void RemoveTextureHost(TextureHost* aTexture);
 
   // Called every time this is composited
   void BumpFlashCounter() {
     mFlashCounter = mFlashCounter >= DIAGNOSTIC_FLASH_COUNTER_MAX
                   ? DIAGNOSTIC_FLASH_COUNTER_MAX : mFlashCounter + 1;
   }
 
-  static PCompositableParent*
-  CreateIPDLActor(CompositableParentManager* mgr,
-                  const TextureInfo& textureInfo);
-
-  static bool DestroyIPDLActor(PCompositableParent* actor);
-
-  static CompositableHost* FromIPDLActor(PCompositableParent* actor);
-
   uint64_t GetCompositorID() const { return mCompositorID; }
 
   const AsyncCompositableRef& GetAsyncRef() const { return mAsyncRef; }
   void SetAsyncRef(const AsyncCompositableRef& aRef) { mAsyncRef = aRef; }
 
   void SetCompositorID(uint64_t aID) { mCompositorID = aID; }
 
   virtual bool Lock() { return false; }
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -345,17 +345,17 @@ ImageHost::Composite(LayerComposite* aLa
       diagnosticFlags |= DiagnosticFlags::YCBCR;
     }
 
     if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
       if (mAsyncRef) {
         ImageCompositeNotificationInfo info;
         info.mImageBridgeProcessId = mAsyncRef.mProcessId;
         info.mNotification = ImageCompositeNotification(
-          mAsyncRef.mAsyncId,
+          mAsyncRef.mHandle,
           img->mTimeStamp, GetCompositor()->GetCompositionTime(),
           img->mFrameID, img->mProducerID);
         static_cast<LayerManagerComposite*>(aLayer->GetLayerManager())->
             AppendImageCompositeNotification(info);
       }
       mLastFrameID = img->mFrameID;
       mLastProducerID = img->mProducerID;
     }
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1193,16 +1193,17 @@ IntSize
 CompositingRenderTargetD3D11::GetSize() const
 {
   return TextureSourceD3D11::GetSize();
 }
 
 SyncObjectD3D11::SyncObjectD3D11(SyncHandle aSyncHandle)
  : mSyncHandle(aSyncHandle)
 {
+  mD3D11Device = DeviceManagerDx::Get()->GetContentDevice();
 }
 
 bool
 SyncObjectD3D11::Init()
 {
   if (mKeyedMutex) {
     return true;
   }
@@ -1234,16 +1235,26 @@ SyncObjectD3D11::Init()
 }
 
 void
 SyncObjectD3D11::RegisterTexture(ID3D11Texture2D* aTexture)
 {
   mD3D11SyncedTextures.push_back(aTexture);
 }
 
+bool
+SyncObjectD3D11::IsSyncObjectValid()
+{
+  RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
+  if (!dev || (dev != mD3D11Device)) {
+    return false;
+  }
+  return true;
+}
+
 void
 SyncObjectD3D11::FinalizeFrame()
 {
   if (!mD3D11SyncedTextures.size()) {
     return;
   }
   if (!Init()) {
     return;
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -410,26 +410,28 @@ private:
   RefPtr<ID3D11RenderTargetView> mRTView;
 };
 
 class SyncObjectD3D11 : public SyncObject
 {
 public:
   explicit SyncObjectD3D11(SyncHandle aSyncHandle);
   virtual void FinalizeFrame();
+  virtual bool IsSyncObjectValid();
 
   virtual SyncType GetSyncType() { return SyncType::D3D11; }
 
   void RegisterTexture(ID3D11Texture2D* aTexture);
 
 private:
   bool Init();
 
 private:
   SyncHandle mSyncHandle;
+  RefPtr<ID3D11Device> mD3D11Device;
   RefPtr<ID3D11Texture2D> mD3D11Texture;
   RefPtr<IDXGIKeyedMutex> mKeyedMutex;
   std::vector<ID3D11Texture2D*> mD3D11SyncedTextures;
 };
 
 inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel)
 {
   int32_t maxTextureSize;
deleted file mode 100644
--- a/gfx/layers/ipc/CompositableForwarder.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "CompositableForwarder.h"
-#include "mozilla/layers/CompositableChild.h"
-
-namespace mozilla {
-namespace layers {
-
-void
-CompositableForwarder::Destroy(CompositableChild* aCompositable)
-{
-  AssertInForwarderThread();
-
-  if (!aCompositable->CanSend()) {
-    return;
-  }
-
-  if (!DestroyInTransaction(aCompositable, false)) {
-    aCompositable->SendDestroy();
-  }
-}
-
-} // namespace layers
-} // namespace mozilla
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -66,20 +66,18 @@ public:
   /**
    * Communicate to the compositor that aRegion in the texture identified by
    * aCompositable and aIdentifier has been updated to aThebesBuffer.
    */
   virtual void UpdateTextureRegion(CompositableClient* aCompositable,
                                    const ThebesBufferData& aThebesBufferData,
                                    const nsIntRegion& aUpdatedRegion) = 0;
 
-  virtual void Destroy(CompositableChild* aCompositable);
-
+  virtual void ReleaseCompositable(const CompositableHandle& aHandle) = 0;
   virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0;
-  virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 0;
 
   /**
    * Tell the CompositableHost on the compositor side to remove the texture
    * from the CompositableHost.
    * This function does not delete the TextureHost corresponding to the
    * TextureClient passed in parameter.
    * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
    * the transaction becomes synchronous.
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -61,17 +61,20 @@ ScheduleComposition(CompositableHost* aC
 }
 
 bool
 CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation& aEdit,
                                                      EditReplyVector& replyv)
 {
   // Ignore all operations on compositables created on stale compositors. We
   // return true because the child is unable to handle errors.
-  CompositableHost* compositable = CompositableHost::FromIPDLActor(aEdit.compositableParent());
+  RefPtr<CompositableHost> compositable = FindCompositable(aEdit.compositable());
+  if (!compositable) {
+    return false;
+  }
   if (compositable->GetCompositor() && !compositable->GetCompositor()->IsValid()) {
     return true;
   }
 
   switch (aEdit.detail().type()) {
     case CompositableOperationDetail::TOpPaintTextureRegion: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint PaintedLayer"));
 
@@ -90,17 +93,17 @@ CompositableParentManager::ReceiveCompos
       if (!compositable->UpdateThebes(bufferData,
                                       op.updatedRegion(),
                                       thebes->GetValidRegion(),
                                       &frontUpdatedRegion))
       {
         return false;
       }
       replyv.push_back(
-        OpContentBufferSwap(aEdit.compositableParent(), nullptr, frontUpdatedRegion));
+        OpContentBufferSwap(aEdit.compositable(), frontUpdatedRegion));
 
       RenderTraceInvalidateEnd(thebes, "FF00FF");
       break;
     }
     case CompositableOperationDetail::TOpUseTiledLayerBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
       const OpUseTiledLayerBuffer& op = aEdit.detail().get_OpUseTiledLayerBuffer();
       TiledContentHost* tiledHost = compositable->AsTiledContentHost();
@@ -224,22 +227,67 @@ void
 CompositableParentManager::DestroyActor(const OpDestroy& aOp)
 {
   switch (aOp.type()) {
     case OpDestroy::TPTextureParent: {
       auto actor = aOp.get_PTextureParent();
       TextureHost::ReceivedDestroy(actor);
       break;
     }
-    case OpDestroy::TPCompositableParent: {
-      auto actor = aOp.get_PCompositableParent();
-      CompositableHost::ReceivedDestroy(actor);
+    case OpDestroy::TCompositableHandle: {
+      ReleaseCompositable(aOp.get_CompositableHandle());
       break;
     }
     default: {
       MOZ_ASSERT(false, "unsupported type");
     }
   }
 }
 
+RefPtr<CompositableHost>
+CompositableParentManager::AddCompositable(const CompositableHandle& aHandle,
+				           const TextureInfo& aInfo)
+{
+  if (mCompositables.find(aHandle.Value()) != mCompositables.end()) {
+    NS_ERROR("Client should not allocate duplicate handles");
+    return nullptr;
+  }
+  if (!aHandle) {
+    NS_ERROR("Client should not allocate 0 as a handle");
+    return nullptr;
+  }
+
+  RefPtr<CompositableHost> host = CompositableHost::Create(aInfo);
+  if (!host) {
+    return nullptr;
+  }
+
+  mCompositables[aHandle.Value()] = host;
+  return host;
+}
+
+RefPtr<CompositableHost>
+CompositableParentManager::FindCompositable(const CompositableHandle& aHandle)
+{
+  auto iter = mCompositables.find(aHandle.Value());
+  if (iter == mCompositables.end()) {
+    return nullptr;
+  }
+  return iter->second;
+}
+
+void
+CompositableParentManager::ReleaseCompositable(const CompositableHandle& aHandle)
+{
+  auto iter = mCompositables.find(aHandle.Value());
+  if (iter == mCompositables.end()) {
+    return;
+  }
+
+  RefPtr<CompositableHost> host = iter->second;
+  mCompositables.erase(iter);
+
+  host->Detach(nullptr, CompositableHost::FORCE_DETACH);
+}
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositableTransactionParent.h
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -7,22 +7,21 @@
 
 #ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 #define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 
 #include <vector>                       // for vector
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
+#include "CompositableHost.h"
 
 namespace mozilla {
 namespace layers {
 
-class CompositableHost;
-
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 // Since PCompositble has two potential manager protocols, we can't just call
 // the Manager() method usually generated when there's one manager protocol,
 // so both manager protocols implement this and we keep a reference to them
 // through this interface.
 class CompositableParentManager : public HostIPCAllocator
 {
@@ -34,22 +33,34 @@ public:
   void UpdateFwdTransactionId(uint64_t aTransactionId)
   {
     MOZ_ASSERT(mFwdTransactionId < aTransactionId);
     mFwdTransactionId = aTransactionId;
   }
 
   uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
 
+  RefPtr<CompositableHost> AddCompositable(
+    const CompositableHandle& aHandle,
+    const TextureInfo& aInfo);
+  RefPtr<CompositableHost> FindCompositable(const CompositableHandle& aHandle);
+
 protected:
   /**
    * Handle the IPDL messages that affect PCompositable actors.
    */
   bool ReceiveCompositableUpdate(const CompositableOperation& aEdit,
                                  EditReplyVector& replyv);
 
+  void ReleaseCompositable(const CompositableHandle& aHandle);
+
   uint64_t mFwdTransactionId = 0;
+
+  /**
+   * Mapping form IDs to CompositableHosts.
+   */
+  std::map<uint64_t, RefPtr<CompositableHost>> mCompositables;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -18,23 +18,21 @@
 #include "mozilla/Monitor.h"            // for Monitor, MonitorAutoLock
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
 #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
-#include "mozilla/layers/CompositableChild.h"
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
-#include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mtransport/runnable_utils.h"
 #include "nsContentUtils.h"
 #include "nsISupportsImpl.h"            // for ImageContainer::AddRef, etc
 #include "nsTArray.h"                   // for AutoTArray, nsTArray, etc
 #include "nsTArrayForwardDeclare.h"     // for AutoTArray
@@ -150,17 +148,17 @@ struct AutoEndTransaction {
   CompositableTransaction* mTxn;
 };
 
 void
 ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
                               const nsTArray<TimedTextureClient>& aTextures)
 {
   MOZ_ASSERT(aCompositable);
-  MOZ_ASSERT(aCompositable->GetIPDLActor());
+  MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aCompositable->IsConnected());
 
   AutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
 
@@ -174,17 +172,17 @@ ImageBridgeChild::UseTextures(Compositab
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         readLock,
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID));
 
     // Wait end of usage on host side if TextureFlags::RECYCLE is set
     HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
-  mTxn->AddNoSwapEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+  mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
                                             OpUseTexture(textures)));
 }
 
 void
 ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                             TextureClient* aTextureOnBlack,
                                             TextureClient* aTextureOnWhite)
 {
@@ -201,18 +199,17 @@ ImageBridgeChild::UseComponentAlphaTextu
   aTextureOnBlack->SerializeReadLock(readLockB);
   aTextureOnWhite->SerializeReadLock(readLockW);
 
   HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
   HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
 
   mTxn->AddNoSwapEdit(
     CompositableOperation(
-      nullptr,
-      aCompositable->GetIPDLActor(),
+      aCompositable->GetIPCHandle(),
       OpUseComponentAlphaTextures(
         nullptr, aTextureOnBlack->GetIPDLActor(),
         nullptr, aTextureOnWhite->GetIPDLActor(),
         readLockB, readLockW
       )
     )
   );
 }
@@ -267,24 +264,16 @@ ImageBridgeChild::ShutdownStep1(Synchron
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(InImageBridgeChildThread(),
              "Should be in ImageBridgeChild thread.");
 
   MediaSystemResourceManager::Shutdown();
 
   // Force all managed protocols to shut themselves down cleanly
-  InfallibleTArray<PCompositableChild*> compositables;
-  ManagedPCompositableChild(compositables);
-  for (int i = compositables.Length() - 1; i >= 0; --i) {
-    auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
-    if (compositable) {
-      compositable->Destroy();
-    }
-  }
   InfallibleTArray<PTextureChild*> textures;
   ManagedPTextureChild(textures);
   for (int i = textures.Length() - 1; i >= 0; --i) {
     RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
     if (client) {
       client->Destroy();
     }
   }
@@ -301,28 +290,27 @@ ImageBridgeChild::ShutdownStep1(Synchron
 // dispatched function
 void
 ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
 {
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(InImageBridgeChildThread(),
              "Should be in ImageBridgeChild thread.");
-
-  if (!mCalledClose) {
-    Close();
-    mCalledClose = true;
-  }
+  Close();
 }
 
 void
 ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mCanSend = false;
-  mCalledClose = true;
+  {
+    MutexAutoLock lock(mContainerMapLock);
+    mImageContainers.Clear();
+  }
 }
 
 void
 ImageBridgeChild::DeallocPImageBridgeChild()
 {
   this->Release();
 }
 
@@ -344,17 +332,17 @@ ImageBridgeChild::CreateCanvasClientSync
                                          RefPtr<CanvasClient>* const outResult)
 {
   AutoCompleteTask complete(aTask);
   *outResult = CreateCanvasClientNow(aType, aFlags);
 }
 
 ImageBridgeChild::ImageBridgeChild()
   : mCanSend(false)
-  , mCalledClose(false)
+  , mDestroyed(false)
   , mFwdTransactionId(0)
   , mContainerMapLock("ImageBridgeChild.mContainerMapLock")
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mTxn = new CompositableTransaction();
 }
 
@@ -386,51 +374,26 @@ ImageBridgeChild::Connect(CompositableCl
   uint64_t id = sNextID++;
 
   {
     MutexAutoLock lock(mContainerMapLock);
     MOZ_ASSERT(!mImageContainers.Contains(id));
     mImageContainers.Put(id, aImageContainer);
   }
 
-  PCompositableChild* child =
-    SendPCompositableConstructor(aCompositable->GetTextureInfo(), id);
-  if (!child) {
-    return;
-  }
-  aCompositable->InitIPDLActor(child, id);
+  CompositableHandle handle(id);
+  aCompositable->InitIPDL(handle);
+  SendNewCompositable(handle, aCompositable->GetTextureInfo());
 }
 
 void
-ImageBridgeChild::ForgetImageContainer(uint64_t aAsyncContainerID)
+ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle)
 {
   MutexAutoLock lock(mContainerMapLock);
-  mImageContainers.Remove(aAsyncContainerID);
-}
-
-PCompositableChild*
-ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, const uint64_t& aID)
-{
-  MOZ_ASSERT(CanSend());
-  return AsyncCompositableChild::CreateActor(aID);
-}
-
-bool
-ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
-{
-  AsyncCompositableChild* actor = static_cast<AsyncCompositableChild*>(aActor);
-  MOZ_ASSERT(actor->GetAsyncID());
-
-  {
-    MutexAutoLock lock(mContainerMapLock);
-    mImageContainers.Remove(actor->GetAsyncID());
-  }
-
-  AsyncCompositableChild::DestroyActor(aActor);
-  return true;
+  mImageContainers.Remove(aHandle.Value());
 }
 
 Thread* ImageBridgeChild::GetThread() const
 {
   return sImageBridgeChildThread;
 }
 
 /* static */ RefPtr<ImageBridgeChild>
@@ -456,17 +419,16 @@ ImageBridgeChild::DispatchReleaseTexture
 
   RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
   if (!imageBridge) {
     // TextureClient::Release should normally happen in the ImageBridgeChild
     // thread because it usually generate some IPDL messages.
     // However, if we take this branch it means that the ImageBridgeChild
     // has already shut down, along with the TextureChild, which means no
     // message will be sent and it is safe to run this code from any thread.
-    MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
     RELEASE_MANUALLY(aClient);
     return;
   }
 
   RefPtr<Runnable> runnable = WrapRunnable(
     imageBridge,
     &ImageBridgeChild::ReleaseTextureClientNow,
     aClient);
@@ -768,16 +730,18 @@ ImageBridgeChild::WillShutdown()
     RefPtr<Runnable> runnable = WrapRunnable(
       RefPtr<ImageBridgeChild>(this),
       &ImageBridgeChild::ShutdownStep2,
       &task);
     GetMessageLoop()->PostTask(runnable.forget());
 
     task.Wait();
   }
+
+  mDestroyed = true;
 }
 
 void
 ImageBridgeChild::InitSameProcess()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
 
   MOZ_ASSERT(!sImageBridgeChildSingleton);
@@ -1030,16 +994,22 @@ ImageBridgeChild::DeallocShmem(ipc::Shme
 {
   if (InImageBridgeChildThread()) {
     if (!CanSend()) {
       return false;
     }
     return PImageBridgeChild::DeallocShmem(aShmem);
   }
 
+  // If we can't post a task, then we definitely cannot send, so there's
+  // no reason to queue up this send.
+  if (!CanPostTask()) {
+    return false;
+  }
+
   SynchronousTask task("AllocatorProxy Dealloc");
   bool result = false;
 
   RefPtr<Runnable> runnable = WrapRunnable(
     RefPtr<ImageBridgeChild>(this),
     &ImageBridgeChild::ProxyDeallocShmemNow,
     &task,
     &aShmem,
@@ -1104,17 +1074,17 @@ ImageBridgeChild::RecvParentAsyncMessage
 mozilla::ipc::IPCResult
 ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
 {
   for (auto& n : aNotifications) {
     RefPtr<ImageContainerListener> listener;
     {
       MutexAutoLock lock(mContainerMapLock);
       ImageContainer* imageContainer;
-      imageContainer = mImageContainers.Get(n.asyncCompositableID());
+      imageContainer = mImageContainers.Get(n.compositable().Value());
       if (imageContainer) {
         listener = imageContainer->GetImageContainerListener();
       }
     }
     if (listener) {
       listener->NotifyComposite(n);
     }
   }
@@ -1149,62 +1119,95 @@ IBCAddOpDestroy(CompositableTransaction*
 
 bool
 ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
 {
   return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
 }
 
 bool
-ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle)
 {
-  return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+  return IBCAddOpDestroy(mTxn, OpDestroy(aHandle), false);
 }
 
-
 void
 ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                 TextureClient* aTexture)
 {
   MOZ_ASSERT(CanSend());
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
   MOZ_ASSERT(aCompositable->IsConnected());
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
 
   CompositableOperation op(
-    nullptr, aCompositable->GetIPDLActor(),
+    aCompositable->GetIPCHandle(),
     OpRemoveTexture(nullptr, aTexture->GetIPDLActor()));
 
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTxn->AddEdit(op);
   } else {
     mTxn->AddNoSwapEdit(op);
   }
 }
 
 bool ImageBridgeChild::IsSameProcess() const
 {
   return OtherPid() == base::GetCurrentProcId();
 }
 
+bool
+ImageBridgeChild::CanPostTask() const
+{
+  // During shutdown, the cycle collector may free objects that are holding a
+  // reference to ImageBridgeChild. Since this happens on the main thread,
+  // ImageBridgeChild will attempt to post a task to the ImageBridge thread.
+  // However the thread manager has already been shut down, so the task cannot
+  // post.
+  //
+  // It's okay if this races. We only care about the shutdown case where
+  // everything's happening on the main thread. Even if it races outside of
+  // shutdown, it's still harmless to post the task, since the task must
+  // check CanSend().
+  return !mDestroyed;
+}
+
 void
-ImageBridgeChild::Destroy(CompositableChild* aCompositable)
+ImageBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle)
 {
   if (!InImageBridgeChildThread()) {
+    // If we can't post a task, then we definitely cannot send, so there's
+    // no reason to queue up this send.
+    if (!CanPostTask()) {
+      return;
+    }
+
     RefPtr<Runnable> runnable = WrapRunnable(
       RefPtr<ImageBridgeChild>(this),
-      &ImageBridgeChild::Destroy,
-      RefPtr<CompositableChild>(aCompositable));
+      &ImageBridgeChild::ReleaseCompositable,
+      aHandle);
     GetMessageLoop()->PostTask(runnable.forget());
     return;
   }
-  CompositableForwarder::Destroy(aCompositable);
+
+  if (!CanSend()) {
+    return;
+  }
+
+  if (!DestroyInTransaction(aHandle)) {
+    SendReleaseCompositable(aHandle);
+  }
+
+  {
+    MutexAutoLock lock(mContainerMapLock);
+    mImageContainers.Remove(aHandle.Value());
+  }
 }
 
 bool
 ImageBridgeChild::CanSend() const
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   return mCanSend;
 }
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -163,20 +163,16 @@ public:
    * Returns the ImageBridgeChild's message loop.
    *
    * Can be called from any thread.
    */
   virtual MessageLoop * GetMessageLoop() const override;
 
   virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
 
-  PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo,
-                                              const uint64_t& aID) override;
-  bool DeallocPCompositableChild(PCompositableChild* aActor) override;
-
   virtual PTextureChild*
   AllocPTextureChild(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aSerial) override;
 
   virtual bool
   DeallocPTextureChild(PTextureChild* actor) override;
 
   PMediaSystemResourceManagerChild*
   AllocPMediaSystemResourceManagerChild() override;
@@ -263,36 +259,36 @@ public:
    * See CompositableForwarder::UseTextures
    */
   virtual void UseTextures(CompositableClient* aCompositable,
                            const nsTArray<TimedTextureClient>& aTextures) override;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) override;
 
-  void Destroy(CompositableChild* aCompositable) override;
+  void ReleaseCompositable(const CompositableHandle& aHandle) override;
 
-  void ForgetImageContainer(uint64_t aAsyncContainerID);
+  void ForgetImageContainer(const CompositableHandle& aHandle);
 
   /**
    * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set.
    * Host side's usage is checked via CompositableRef.
    */
   void HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient);
 
   /**
    * Notify id of Texture When host side end its use. Transaction id is used to
    * make sure if there is no newer usage.
    */
   void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
 
   virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
 
   virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
-  virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+  bool DestroyInTransaction(const CompositableHandle& aHandle);
 
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                              TextureClient* aTexture) override;
 
   virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTileLayerDescriptor) override
   {
     MOZ_CRASH("should not be called");
@@ -359,24 +355,25 @@ protected:
   void ShutdownStep1(SynchronousTask* aTask);
   void ShutdownStep2(SynchronousTask* aTask);
   void MarkShutDown();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPImageBridgeChild() override;
 
   bool CanSend() const;
+  bool CanPostTask() const;
 
   static void ShutdownSingleton();
 
 private:
   CompositableTransaction* mTxn;
 
   bool mCanSend;
-  bool mCalledClose;
+  mozilla::Atomic<bool> mDestroyed;
 
   /**
    * Transaction id of CompositableForwarder.
    * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
    */
   uint64_t mFwdTransactionId;
 
   /**
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -15,17 +15,16 @@
 #include "mozilla/HalTypes.h"           // for hal::THREAD_PRIORITY_COMPOSITOR
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply
-#include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Unused.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for ImageBridgeParent::Release, etc
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
@@ -100,16 +99,17 @@ ImageBridgeParent::CreateForGPUProcess(E
   return true;
 }
 
 void
 ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Can't alloc/dealloc shmems from now on.
   mClosed = true;
+  mCompositables.clear();
 
   MessageLoop::current()->PostTask(NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy));
 
   // It is very important that this method gets called at shutdown (be it a clean
   // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef.
   // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent
   // is leaked which causes the CompositorThreadHolder to be leaked and
   // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting
@@ -227,44 +227,33 @@ mozilla::ipc::IPCResult ImageBridgeParen
   ManagedPTextureParent(textures);
   for (unsigned int i = 0; i < textures.Length(); ++i) {
     RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
     tex->DeallocateDeviceData();
   }
   return IPC_OK();
 }
 
-PCompositableParent*
-ImageBridgeParent::AllocPCompositableParent(const TextureInfo& aInfo, const uint64_t& aID)
+mozilla::ipc::IPCResult
+ImageBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle, const TextureInfo& aInfo)
 {
-  PCompositableParent* actor = CompositableHost::CreateIPDLActor(this, aInfo);
-  if (mCompositables.find(aID) != mCompositables.end()) {
-    NS_ERROR("Async compositable ID already exists");
-    return actor;
-  }
-  if (!aID) {
-    NS_ERROR("Expected non-zero async compositable ID");
-    return actor;
+  RefPtr<CompositableHost> host = AddCompositable(aHandle, aInfo);
+  if (!host) {
+    return IPC_FAIL_NO_REASON(this);
   }
 
-  CompositableHost* host = CompositableHost::FromIPDLActor(actor);
-
-  host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aID));
-  mCompositables[aID] = host;
-
-  return actor;
+  host->SetAsyncRef(AsyncCompositableRef(OtherPid(), aHandle));
+  return IPC_OK();
 }
 
-bool ImageBridgeParent::DeallocPCompositableParent(PCompositableParent* aActor)
+mozilla::ipc::IPCResult
+ImageBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
 {
-  if (CompositableHost* host = CompositableHost::FromIPDLActor(aActor)) {
-    const AsyncCompositableRef& ref = host->GetAsyncRef();
-    mCompositables.erase(ref.mAsyncId);
-  }
-  return CompositableHost::DestroyIPDLActor(aActor);
+  ReleaseCompositable(aHandle);
+  return IPC_OK();
 }
 
 PTextureParent*
 ImageBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                        const LayersBackend& aLayersBackend,
                                        const TextureFlags& aFlags,
                                        const uint64_t& aSerial)
 {
@@ -319,17 +308,17 @@ ImageBridgeParent::NotifyImageComposites
   // notifications in one message per group.
   aNotifications.Sort(ProcessIdComparator());
   uint32_t i = 0;
   bool ok = true;
   while (i < aNotifications.Length()) {
     AutoTArray<ImageCompositeNotification,1> notifications;
     notifications.AppendElement(aNotifications[i].mNotification);
     uint32_t end = i + 1;
-    MOZ_ASSERT(aNotifications[i].mNotification.asyncCompositableID());
+    MOZ_ASSERT(aNotifications[i].mNotification.compositable());
     ProcessId pid = aNotifications[i].mImageBridgeProcessId;
     while (end < aNotifications.Length() &&
            aNotifications[end].mImageBridgeProcessId == pid) {
       notifications.AppendElement(aNotifications[end].mNotification);
       ++end;
     }
     GetInstance(pid)->SendPendingAsyncMessages();
     if (!GetInstance(pid)->SendDidComposite(notifications)) {
@@ -433,20 +422,10 @@ ImageBridgeParent::NotifyNotUsed(PTextur
   mPendingAsyncMessage.push_back(
     OpNotifyNotUsed(textureId, aTransactionId));
 
   if (!IsAboutToSendAsyncMessages()) {
     SendPendingAsyncMessages();
   }
 }
 
-CompositableHost*
-ImageBridgeParent::FindCompositable(uint64_t aId)
-{
-  auto iter = mCompositables.find(aId);
-  if (iter == mCompositables.end()) {
-    return nullptr;
-  }
-  return iter->second;
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -72,26 +72,26 @@ public:
   // PImageBridge
   virtual mozilla::ipc::IPCResult RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                                           const uint64_t& aFwdTransactionId,
                                           EditReplyArray* aReply) override;
   virtual mozilla::ipc::IPCResult RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                                                 const uint64_t& aFwdTransactionId) override;
 
-  PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo,
-                                                const uint64_t& aID) override;
-  bool DeallocPCompositableParent(PCompositableParent* aActor) override;
-
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aSerial) override;
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
+  virtual mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
+                                                      const TextureInfo& aInfo) override;
+  virtual mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
+
   PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override;
   bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override;
 
   // Shutdown step 1
   virtual mozilla::ipc::IPCResult RecvWillClose() override;
 
   MessageLoop* GetMessageLoop() const { return mMessageLoop; }
 
@@ -118,18 +118,16 @@ public:
   static ImageBridgeParent* GetInstance(ProcessId aId);
 
   static bool NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo>& aNotifications);
 
   virtual bool UsesImageBridge() const override { return true; }
 
   virtual bool IPCOpen() const override { return !mClosed; }
 
-  CompositableHost* FindCompositable(uint64_t aId);
-
 protected:
   void OnChannelConnected(int32_t pid) override;
 
   void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
 
 private:
   void DeferredDestroy();
   MessageLoop* mMessageLoop;
@@ -143,41 +141,14 @@ private:
   /**
    * Map of all living ImageBridgeParent instances
    */
   static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
 
   static MessageLoop* sMainLoop;
 
   RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
-
-  /**
-   * PCompositable and PLayer can, in the case of async textures, be managed by
-   * different top level protocols. In this case they don't share the same
-   * communication channel and we can't send an OpAttachCompositable (PCompositable,
-   * PLayer) message.
-   *
-   * In order to attach a layer and the right compositable if the the compositable
-   * is async, we store references to the async compositables in a CompositableMap
-   * that is accessed only on the compositor thread. During a layer transaction we
-   * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
-   * side we lookup the ID in the map and attach the corresponding compositable to
-   * the layer.
-   *
-   * CompositableMap must be global because the image bridge doesn't have any
-   * reference to whatever we have created with PLayerTransaction. So, the only way to
-   * actually connect these two worlds is to have something global that they can
-   * both query (in the same  thread). The map is not allocated the map on the
-   * stack to avoid the badness of static initialization.
-   *
-   * Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
-   * ImageBridge is used by all the existing compositors that have a video, so
-   * there isn't an instance or "something" that lives outside the boudaries of a
-   * given layer manager on the compositor thread except the image bridge and the
-   * thread itself.
-   */
-  std::map<uint64_t, CompositableHost*> mCompositables;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // gfx_layers_ipc_ImageBridgeParent_h_
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -2,18 +2,16 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 "LayerTransactionChild.h"
 #include "mozilla/gfx/Logging.h"
-#include "mozilla/layers/CompositableChild.h"
-#include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsTArray.h"                   // for nsTArray
 #include "mozilla/layers/TextureClient.h"
 
 namespace mozilla {
 namespace layers {
@@ -31,31 +29,16 @@ LayerTransactionChild::Destroy()
   // ShadowLayerForwarder's destructor.
   // When it happens, IPCOpen() is still true.
   // See bug 1004191.
   mDestroyed = true;
 
   SendShutdown();
 }
 
-
-PCompositableChild*
-LayerTransactionChild::AllocPCompositableChild(const TextureInfo& aInfo)
-{
-  MOZ_ASSERT(!mDestroyed);
-  return CompositableChild::CreateActor();
-}
-
-bool
-LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor)
-{
-  CompositableChild::DestroyActor(actor);
-  return true;
-}
-
 void
 LayerTransactionChild::ActorDestroy(ActorDestroyReason why)
 {
   mDestroyed = true;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/LayerTransactionChild.h
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -52,19 +52,16 @@ protected:
   explicit LayerTransactionChild(const uint64_t& aId)
     : mForwarder(nullptr)
     , mIPCOpen(false)
     , mDestroyed(false)
     , mId(aId)
   {}
   ~LayerTransactionChild() { }
 
-  virtual PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo) override;
-  virtual bool DeallocPCompositableChild(PCompositableChild* actor) override;
-
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   void AddIPDLReference() {
     MOZ_ASSERT(mIPCOpen == false);
     mIPCOpen = true;
     AddRef();
   }
   void ReleaseIPDLReference() {
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -19,17 +19,16 @@
 #include "mozilla/layers/ColorLayerComposite.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ContainerLayerComposite.h"
 #include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
 #include "mozilla/layers/ImageLayerComposite.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
-#include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/PaintedLayerComposite.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "mozilla/Unused.h"
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsDeviceContext.h"            // for AppUnitsPerCSSPixel
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
@@ -90,16 +89,17 @@ LayerTransactionParent::RecvShutdown()
   }
   return IPC_OK();
 }
 
 void
 LayerTransactionParent::Destroy()
 {
   mDestroyed = true;
+  mCompositables.clear();
 }
 
 mozilla::ipc::IPCResult
 LayerTransactionParent::RecvUpdateNoSwap(const TransactionInfo& txn)
 {
   return RecvUpdate(txn, nullptr);
 }
 
@@ -569,17 +569,17 @@ LayerTransactionParent::RecvUpdate(const
       if (!ReceiveCompositableUpdate(edit.get_CompositableOperation(),
                                 replyv)) {
         return IPC_FAIL_NO_REASON(this);
       }
       break;
     }
     case Edit::TOpAttachCompositable: {
       const OpAttachCompositable& op = edit.get_OpAttachCompositable();
-      CompositableHost* host = CompositableHost::FromIPDLActor(op.compositableParent());
+      RefPtr<CompositableHost> host = FindCompositable(op.compositable());
       if (mPendingCompositorUpdates) {
         // Do not attach compositables from old layer trees. Return true since
         // content cannot handle errors.
         return IPC_OK();
       }
       if (!Attach(AsLayer(op.layer()), host, false)) {
         return IPC_FAIL_NO_REASON(this);
       }
@@ -594,17 +594,17 @@ LayerTransactionParent::RecvUpdate(const
         // Do not attach compositables from old layer trees. Return true since
         // content cannot handle errors.
         return IPC_OK();
       }
       ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(OtherPid());
       if (!imageBridge) {
         return IPC_FAIL_NO_REASON(this);
       }
-      CompositableHost* host = imageBridge->FindCompositable(op.containerID());
+      RefPtr<CompositableHost> host = imageBridge->FindCompositable(op.compositable());
       if (!host) {
         NS_ERROR("CompositableHost not found in the map");
         return IPC_FAIL_NO_REASON(this);
       }
       if (!Attach(AsLayer(op.layer()), host, true)) {
         return IPC_FAIL_NO_REASON(this);
       }
       if (mLayerManager->GetCompositor()) {
@@ -937,28 +937,16 @@ LayerTransactionParent::RecvClearCachedR
 
 mozilla::ipc::IPCResult
 LayerTransactionParent::RecvForceComposite()
 {
   mCompositorBridge->ForceComposite(this);
   return IPC_OK();
 }
 
-PCompositableParent*
-LayerTransactionParent::AllocPCompositableParent(const TextureInfo& aInfo)
-{
-  return CompositableHost::CreateIPDLActor(this, aInfo);
-}
-
-bool
-LayerTransactionParent::DeallocPCompositableParent(PCompositableParent* aActor)
-{
-  return CompositableHost::DestroyIPDLActor(aActor);
-}
-
 void
 LayerTransactionParent::ActorDestroy(ActorDestroyReason why)
 {
 }
 
 bool
 LayerTransactionParent::AllocShmem(size_t aSize,
                                    ipc::SharedMemory::SharedMemoryType aType,
@@ -1036,24 +1024,40 @@ LayerTransactionParent::AsLayer(const La
 {
   if (!aHandle) {
     return nullptr;
   }
   return mLayerMap.Get(aHandle.Value()).get();
 }
 
 mozilla::ipc::IPCResult
+LayerTransactionParent::RecvNewCompositable(const CompositableHandle& aHandle, const TextureInfo& aInfo)
+{
+  if (!AddCompositable(aHandle, aInfo)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 LayerTransactionParent::RecvReleaseLayer(const LayerHandle& aHandle)
 {
   if (!aHandle || !mLayerMap.Contains(aHandle.Value())) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   Maybe<RefPtr<Layer>> maybeLayer = mLayerMap.GetAndRemove(aHandle.Value());
   if (maybeLayer) {
     (*maybeLayer)->Disconnect();
   }
 
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+LayerTransactionParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
+{
+  ReleaseCompositable(aHandle);
+  return IPC_OK();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -117,17 +117,20 @@ protected:
                                                 const TimeDuration& aPaintTime) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdate(const TransactionInfo& aInfo,
                                              EditReplyArray* reply) override;
 
   virtual mozilla::ipc::IPCResult RecvUpdateNoSwap(const TransactionInfo& aInfo) override;
 
   virtual mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
+  virtual mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
+                                                      const TextureInfo& aInfo) override;
   virtual mozilla::ipc::IPCResult RecvReleaseLayer(const LayerHandle& aHandle) override;
+  virtual mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   virtual mozilla::ipc::IPCResult RecvClearCachedResources() override;
   virtual mozilla::ipc::IPCResult RecvForceComposite() override;
   virtual mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime) override;
   virtual mozilla::ipc::IPCResult RecvLeaveTestMode() override;
   virtual mozilla::ipc::IPCResult RecvGetAnimationOpacity(const LayerHandle& aLayerHandle,
                                                           float* aOpacity,
                                                           bool* aHasAnimationOpacity) override;
@@ -139,19 +142,16 @@ protected:
   virtual mozilla::ipc::IPCResult RecvSetAsyncZoom(const FrameMetrics::ViewID& aId,
                                                    const float& aValue) override;
   virtual mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
   virtual mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData) override;
   virtual mozilla::ipc::IPCResult RecvRequestProperty(const nsString& aProperty, float* aValue) override;
   virtual mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
                                                              nsTArray<ScrollableLayerGuid>&& aTargets) override;
 
-  virtual PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo) override;
-  virtual bool DeallocPCompositableParent(PCompositableParent* actor) override;
-
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   template <typename T>
   bool BindLayer(const RefPtr<Layer>& aLayer, const T& aCreateOp) {
     return BindLayerToHandle(aLayer, aCreateOp.layer());
   }
 
   bool BindLayerToHandle(RefPtr<Layer> aLayer, const LayerHandle& aHandle);
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 LayersSurfaces;
-include protocol PCompositable;
 include protocol PCompositorBridge;
 include protocol PRenderFrame;
 include protocol PTexture;
 
 include "gfxipc/ShadowLayerUtils.h";
 include "mozilla/GfxMessageUtils.h";
 include "ImageLayers.h";
 
@@ -45,16 +44,17 @@ using struct mozilla::layers::ScrollMeta
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
 using mozilla::gfx::Glyph from "Layers.h";
 using mozilla::layers::BorderColors from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::BorderCorners from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::BorderWidths from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 struct TargetConfig {
   IntRect naturalBounds;
   ScreenRotation rotation;
   ScreenOrientationInternal orientation;
@@ -68,22 +68,22 @@ struct OpCreateImageLayer      { LayerHa
 struct OpCreateColorLayer      { LayerHandle layer; };
 struct OpCreateTextLayer       { LayerHandle layer; };
 struct OpCreateBorderLayer       { LayerHandle layer; };
 struct OpCreateCanvasLayer     { LayerHandle layer; };
 struct OpCreateRefLayer        { LayerHandle layer; };
 
 struct OpAttachCompositable {
   LayerHandle layer;
-  PCompositable compositable;
+  CompositableHandle compositable;
 };
 
 struct OpAttachAsyncCompositable {
   LayerHandle layer;
-  uint64_t containerID;
+  CompositableHandle compositable;
 };
 
 struct ThebesBufferData {
   IntRect rect;
   IntPoint rotation;
 };
 
 struct CubicBezierFunction {
@@ -464,17 +464,17 @@ union CompositableOperationDetail {
   OpRemoveTexture;
 
   OpUseTexture;
   OpUseComponentAlphaTextures;
   OpUseOverlaySource;
 };
 
 struct CompositableOperation {
-  PCompositable compositable;
+  CompositableHandle compositable;
   CompositableOperationDetail detail;
 };
 
 // A unit of a changeset; a set of these comprise a changeset
 // If adding a new edit type that requires the hit testing tree to be updated,
 // set the updateHitTestingTree flag to true in RecvUpdate()
 union Edit {
   OpCreatePaintedLayer;
@@ -502,32 +502,32 @@ union Edit {
 
   CompositableOperation;
 };
 
 // Operations related to destroying resources, always handled after the other
 // operations for safety.
 union OpDestroy {
   PTexture;
-  PCompositable;
+  CompositableHandle;
 };
 
 // Replies to operations
 
 struct OpContentBufferSwap {
-  PCompositable compositable;
+  CompositableHandle compositable;
   nsIntRegion frontUpdatedRegion;
 };
 
 /**
  * An ImageCompositeNotification is sent the first time a particular
  * image is composited by an ImageHost.
  */
 struct ImageCompositeNotification {
-  uint64_t asyncCompositableID;
+  CompositableHandle compositable;
   TimeStamp imageTimeStamp;
   TimeStamp firstCompositeTimeStamp;
   uint32_t frameID;
   uint32_t producerID;
 };
 
 // Unit of a "changeset reply".  This is a weird abstraction, probably
 // only to be used for buffer swapping.
deleted file mode 100644
--- a/gfx/layers/ipc/PCompositable.ipdl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=8 et :
- */
-/* 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 protocol PLayerTransaction;
-include protocol PImageBridge;
-include protocol PCompositorBridge;
-
-namespace mozilla {
-namespace layers {
-
-async protocol PCompositable
-{
-    manager PImageBridge or PLayerTransaction;
-child:
-    async __delete__();
-parent:
-    /**
-     * Asynchronously tell the compositor side to remove the texture.
-     */
-    async Destroy();
-};
-
-} // namespace
-} // namespace
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -6,17 +6,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
 include PlatformWidgetTypes;
 include protocol PAPZ;
 include protocol PAPZCTreeManager;
 include protocol PBrowser;
-include protocol PCompositable;
 include protocol PCompositorWidget;
 include protocol PLayerTransaction;
 include protocol PTexture;
 include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -1,38 +1,37 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 include LayersSurfaces;
 include LayersMessages;
-include protocol PCompositable;
 include protocol PTexture;
 include ProtocolTypes;
 include protocol PMediaSystemResourceManager;
 
 include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 
 using PlatformThreadId from "base/platform_thread.h";
 
 namespace mozilla {
 namespace layers {
 
 /**
  * The PImageBridge protocol is used to allow isolated threads or processes to push
  * frames directly to the compositor thread/process without relying on the main thread
  * which might be too busy dealing with content script.
  */
 sync protocol PImageBridge
 {
-  manages PCompositable;
   manages PTexture;
   manages PMediaSystemResourceManager;
 
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
   async DidComposite(ImageCompositeNotification[] aNotifications);
 
@@ -48,18 +47,19 @@ parent:
   // in a state in which it can't send asynchronous messages
   // so as to not race with the channel getting closed.
   // In the child side, the Closing the channel does not happen right after WillClose,
   // it is scheduled in the ImageBridgeChild's message queue in order to ensure
   // that all of the messages from the parent side have been received and processed
   // before sending closing the channel.
   sync WillClose();
 
-  async PCompositable(TextureInfo aInfo, uint64_t aId);
   async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t aSerial);
   async PMediaSystemResourceManager();
 
+  sync NewCompositable(CompositableHandle aHandle, TextureInfo aInfo);
+  async ReleaseCompositable(CompositableHandle aHandle);
 };
 
 
 } // namespace
 } // namespace
 
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -2,30 +2,30 @@
  * vim: sw=2 ts=8 et :
  */
 /* 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 LayersSurfaces;
 include LayersMessages;
-include protocol PCompositable;
 include protocol PCompositorBridge;
 include protocol PRenderFrame;
 include protocol PTexture;
 
 include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
  * subtree into its own tree), and atomically updating a published
  * subtree.  ("Atomic" in this sense is wrt painting.)
  */
@@ -43,35 +43,36 @@ union MaybeTransform {
  * The "browser" can be a top-level browser window, in which case the PLayer-
  * TransactionChild exists in the UI process. The "browser" can also be a content
  * tab, in which case the PLayerTransactionChild exists in the content process.
  * In either case, the PLayerTransactionParent exists in the GPU process (if
  * there is one) or the UI process otherwise.
  */
 sync protocol PLayerTransaction {
   manager PCompositorBridge;
-  manages PCompositable;
 
 parent:
-  async PCompositable(TextureInfo aTextureInfo);
-
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
   sync Update(TransactionInfo txn) returns (EditReply[] reply);
 
   async PaintTime(uint64_t id, TimeDuration paintTime);
 
   // We don't need to send a sync transaction if
   // no transaction operate require a swap.
   async UpdateNoSwap(TransactionInfo txn);
 
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
 
-  // Release a layer that is no longer in use.
+  // Create a new Compositable.
+  async NewCompositable(CompositableHandle handle, TextureInfo info);
+
+  // Release an object that is no longer in use.
   async ReleaseLayer(LayerHandle layer);
+  async ReleaseCompositable(CompositableHandle compositable);
 
   // Testing APIs
 
   // Enter test mode, set the sample time to sampleTime, and resample
   // animations. sampleTime must not be null.
   sync SetTestSampleTime(TimeStamp sampleTime);
   // Leave test mode and resume normal compositing
   sync LeaveTestMode();
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -17,23 +17,23 @@
 #include "gfxPlatform.h"                // for gfxImageFormat, gfxPlatform
 //#include "gfxSharedImageSurface.h"      // for gfxSharedImageSurface
 #include "ipc/IPCMessageUtils.h"        // for gfxContentType, null_t
 #include "IPDLActor.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient, etc
 #include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ContentClient.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/LayersMessages.h"  // for Edit, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/LayerTransactionChild.h"
-#include "mozilla/layers/PCompositableChild.h"
 #include "mozilla/layers/PTextureChild.h"
 #include "ShadowLayerUtils.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsTArray.h"                   // for AutoTArray, nsTArray, etc
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #include "mozilla/ReentrantMonitor.h"
 
@@ -377,34 +377,34 @@ ShadowLayerForwarder::UseTiledLayerBuffe
                                           const SurfaceDescriptorTiles& aTileLayerDescriptor)
 {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
-  mTxn->AddNoSwapPaint(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+  mTxn->AddNoSwapPaint(CompositableOperation(aCompositable->GetIPCHandle(),
                                              OpUseTiledLayerBuffer(aTileLayerDescriptor)));
 }
 
 void
 ShadowLayerForwarder::UpdateTextureRegion(CompositableClient* aCompositable,
                                           const ThebesBufferData& aThebesBufferData,
                                           const nsIntRegion& aUpdatedRegion)
 {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   mTxn->AddPaint(
     CompositableOperation(
-      nullptr, aCompositable->GetIPDLActor(),
+      aCompositable->GetIPCHandle(),
       OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
 }
 
 void
 ShadowLayerForwarder::UseTextures(CompositableClient* aCompositable,
                                   const nsTArray<TimedTextureClient>& aTextures)
 {
   MOZ_ASSERT(aCompositable);
@@ -430,51 +430,51 @@ ShadowLayerForwarder::UseTextures(Compos
 
       // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
       // race with updates on the main thread. In this case we want the transaction
       // to be synchronous.
       mTxn->MarkSyncTransaction();
     }
     mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
-  mTxn->AddEdit(CompositableOperation(nullptr, aCompositable->GetIPDLActor(),
+  mTxn->AddEdit(CompositableOperation(aCompositable->GetIPCHandle(),
                                       OpUseTexture(textures)));
 }
 
 void
 ShadowLayerForwarder::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                                 TextureClient* aTextureOnBlack,
                                                 TextureClient* aTextureOnWhite)
 {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   MOZ_ASSERT(aTextureOnWhite);
   MOZ_ASSERT(aTextureOnBlack);
-  MOZ_ASSERT(aCompositable->GetIPDLActor());
+  MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
   MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
   MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
   MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
 
   ReadLockDescriptor readLockW;
   ReadLockDescriptor readLockB;
   aTextureOnBlack->SerializeReadLock(readLockB);
   aTextureOnWhite->SerializeReadLock(readLockW);
 
   mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
   mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
 
   mTxn->AddEdit(
     CompositableOperation(
-      nullptr, aCompositable->GetIPDLActor(),
+      aCompositable->GetIPCHandle(),
       OpUseComponentAlphaTextures(
         nullptr, aTextureOnBlack->GetIPDLActor(),
         nullptr, aTextureOnWhite->GetIPDLActor(),
         readLockB, readLockW)
       )
     );
 }
 
@@ -495,19 +495,19 @@ AddOpDestroy(Transaction* aTxn, const Op
 
 bool
 ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
 {
   return AddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
 }
 
 bool
-ShadowLayerForwarder::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+ShadowLayerForwarder::DestroyInTransaction(const CompositableHandle& aHandle)
 {
-  return AddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+  return AddOpDestroy(mTxn, OpDestroy(aHandle), false);
 }
 
 void
 ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                     TextureClient* aTexture)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
@@ -515,17 +515,17 @@ ShadowLayerForwarder::RemoveTextureFromC
   MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
   if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
     // We don't have an actor anymore, don't try to use it!
     return;
   }
 
   mTxn->AddEdit(
     CompositableOperation(
-      nullptr, aCompositable->GetIPDLActor(),
+      aCompositable->GetIPCHandle(),
       OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTxn->MarkSyncTransaction();
   }
 }
 
 bool
 ShadowLayerForwarder::InWorkerThread()
@@ -554,18 +554,17 @@ ShadowLayerForwarder::SendPaintTime(uint
 {
   if (!IPCOpen() ||
       !mShadowManager->SendPaintTime(aId, aPaintTime)) {
     NS_WARNING("Could not send paint times over IPC");
   }
 }
 
 bool
-ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
-                                     const nsIntRegion& aRegionToClear,
+ShadowLayerForwarder::EndTransaction(const nsIntRegion& aRegionToClear,
                                      uint64_t aId,
                                      bool aScheduleComposite,
                                      uint32_t aPaintSequenceNumber,
                                      bool aIsRepeatTransaction,
                                      const mozilla::TimeStamp& aTransactionStart,
                                      bool* aSent)
 {
   *aSent = false;
@@ -728,23 +727,26 @@ ShadowLayerForwarder::EndTransaction(Inf
   info.targetConfig() = targetConfig;
 
   if (!GetTextureForwarder()->IsSameProcess()) {
     MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
     PlatformSyncBeforeUpdate();
   }
 
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END);
+
+  AutoTArray<EditReply, 10> replies;
   if (mTxn->mSwapRequired) {
     MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
     RenderTraceScope rendertrace3("Forward Transaction", "000093");
-    if (!mShadowManager->SendUpdate(info, aReplies)) {
+    if (!mShadowManager->SendUpdate(info, &replies)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
       return false;
     }
+    ProcessReplies(replies);
   } else {
     // If we don't require a swap we can call SendUpdateNoSwap which
     // assumes that aReplies is empty (DEBUG assertion)
     MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
     RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
     if (!mShadowManager->SendUpdateNoSwap(info)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
       return false;
@@ -754,16 +756,49 @@ ShadowLayerForwarder::EndTransaction(Inf
   *aSent = true;
   mIsFirstPaint = false;
   mPaintSyncId = 0;
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
   return true;
 }
 
 void
+ShadowLayerForwarder::ProcessReplies(const nsTArray<EditReply>& aReplies)
+{
+  for (const auto& reply : aReplies) {
+    switch (reply.type()) {
+    case EditReply::TOpContentBufferSwap: {
+      MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
+
+      const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
+
+      RefPtr<CompositableClient> compositable = FindCompositable(obs.compositable());
+      ContentClientRemote* contentClient = compositable->AsContentClientRemote();
+      MOZ_ASSERT(contentClient);
+
+      contentClient->SwapBuffers(obs.frontUpdatedRegion());
+      break;
+    }
+    default:
+      MOZ_CRASH("not reached");
+    }
+  }
+}
+
+RefPtr<CompositableClient>
+ShadowLayerForwarder::FindCompositable(const CompositableHandle& aHandle)
+{
+  CompositableClient* client = nullptr;
+  if (!mCompositables.Get(aHandle.Value(), &client)) {
+    return nullptr;
+  }
+  return client;
+}
+
+void
 ShadowLayerForwarder::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
 {
   if (!IPCOpen()) {
     return;
   }
   Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch);
 }
 
@@ -809,38 +844,41 @@ ShadowLayerForwarder::Connect(Compositab
 #ifdef GFX_COMPOSITOR_LOGGING
   printf("ShadowLayerForwarder::Connect(Compositable)\n");
 #endif
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(mShadowManager);
   if (!IPCOpen()) {
     return;
   }
-  PCompositableChild* actor =
-    mShadowManager->SendPCompositableConstructor(aCompositable->GetTextureInfo());
-  if (!actor) {
-    return;
-  }
-  aCompositable->InitIPDLActor(actor);
+
+  static uint64_t sNextID = 1;
+  uint64_t id = sNextID++;
+
+  mCompositables.Put(id, aCompositable);
+
+  CompositableHandle handle(id);
+  aCompositable->InitIPDL(handle);
+  mShadowManager->SendNewCompositable(handle, aCompositable->GetTextureInfo());
 }
 
 void ShadowLayerForwarder::Attach(CompositableClient* aCompositable,
                                   ShadowableLayer* aLayer)
 {
   MOZ_ASSERT(aLayer);
   MOZ_ASSERT(aCompositable);
-  mTxn->AddEdit(OpAttachCompositable(Shadow(aLayer), nullptr, aCompositable->GetIPDLActor()));
+  mTxn->AddEdit(OpAttachCompositable(Shadow(aLayer), aCompositable->GetIPCHandle()));
 }
 
-void ShadowLayerForwarder::AttachAsyncCompositable(uint64_t aCompositableID,
+void ShadowLayerForwarder::AttachAsyncCompositable(const CompositableHandle& aHandle,
                                                    ShadowableLayer* aLayer)
 {
   MOZ_ASSERT(aLayer);
-  MOZ_ASSERT(aCompositableID != 0); // zero is always an invalid compositable id.
-  mTxn->AddEdit(OpAttachAsyncCompositable(Shadow(aLayer), aCompositableID));
+  MOZ_ASSERT(aHandle);
+  mTxn->AddEdit(OpAttachAsyncCompositable(Shadow(aLayer), aHandle));
 }
 
 void ShadowLayerForwarder::SetShadowManager(PLayerTransactionChild* aShadowManager)
 {
   mShadowManager = static_cast<LayerTransactionChild*>(aShadowManager);
   mShadowManager->SetForwarder(this);
 }
 
@@ -1044,16 +1082,26 @@ void
 ShadowLayerForwarder::SyncWithCompositor()
 {
   auto compositorBridge = GetCompositorBridgeChild();
   if (compositorBridge && compositorBridge->IPCOpen()) {
     compositorBridge->SendSyncWithCompositor();
   }
 }
 
+void
+ShadowLayerForwarder::ReleaseCompositable(const CompositableHandle& aHandle)
+{
+  AssertInForwarderThread();
+  if (!DestroyInTransaction(aHandle)) {
+    mShadowManager->SendReleaseCompositable(aHandle);
+  }
+  mCompositables.Remove(aHandle.Value());
+}
+
 ShadowableLayer::~ShadowableLayer()
 {
   if (mShadow) {
     mForwarder->ReleaseLayer(GetShadow());
   }
 }
 
 } // namespace layers
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -177,17 +177,17 @@ public:
   /**
    * Adds an edit in the transaction in order to attach a Compositable that
    * is not managed by this ShadowLayerForwarder (for example, by ImageBridge
    * in the case of async-video).
    * Since the compositable is not managed by this forwarder, we can't use
    * the compositable or it's IPDL actor here, so we use an ID instead, that
    * is matched on the compositor side.
    */
-  void AttachAsyncCompositable(uint64_t aCompositableID,
+  void AttachAsyncCompositable(const CompositableHandle& aHandle,
                                ShadowableLayer* aLayer);
 
   /**
    * Begin recording a transaction to be forwarded atomically to a
    * LayerManagerComposite.
    */
   void BeginTransaction(const gfx::IntRect& aTargetBounds,
                         ScreenRotation aRotation,
@@ -246,18 +246,19 @@ public:
                ShadowableLayer* aMaskLayer);
 
   /**
    * See CompositableForwarder::UseTiledLayerBuffer
    */
   void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
 
+  void ReleaseCompositable(const CompositableHandle& aHandle) override;
   bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
-  bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+  bool DestroyInTransaction(const CompositableHandle& aHandle);
 
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                              TextureClient* aTexture) override;
 
   /**
    * Communicate to the compositor that aRegion in the texture identified by aLayer
    * and aIdentifier has been updated to aThebesBuffer.
    */
@@ -279,18 +280,17 @@ public:
    */
   void SendPaintTime(uint64_t aId, TimeDuration aPaintTime);
 
   /**
    * End the current transaction and forward it to LayerManagerComposite.
    * |aReplies| are directions from the LayerManagerComposite to the
    * caller of EndTransaction().
    */
-  bool EndTransaction(InfallibleTArray<EditReply>* aReplies,
-                      const nsIntRegion& aRegionToClear,
+  bool EndTransaction(const nsIntRegion& aRegionToClear,
                       uint64_t aId,
                       bool aScheduleComposite,
                       uint32_t aPaintSequenceNumber,
                       bool aIsRepeatTransaction,
                       const mozilla::TimeStamp& aTransactionStart,
                       bool* aSent);
 
   /**
@@ -415,16 +415,20 @@ protected:
   explicit ShadowLayerForwarder(ClientLayerManager* aClientLayerManager);
 
 #ifdef DEBUG
   void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const;
 #else
   void CheckSurfaceDescriptor(const SurfaceDescriptor* aDescriptor) const {}
 #endif
 
+  void ProcessReplies(const nsTArray<EditReply>& aReplies);
+
+  RefPtr<CompositableClient> FindCompositable(const CompositableHandle& aHandle);
+
   bool InWorkerThread();
 
   CompositorBridgeChild* GetCompositorBridgeChild();
 
   RefPtr<LayerTransactionChild> mShadowManager;
   RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
 
 private:
@@ -434,16 +438,17 @@ private:
   MessageLoop* mMessageLoop;
   DiagnosticTypes mDiagnosticTypes;
   bool mIsFirstPaint;
   bool mWindowOverlayChanged;
   int32_t mPaintSyncId;
   InfallibleTArray<PluginWindowData> mPluginWindowData;
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
   uint64_t mNextLayerHandle;
+  nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
 };
 
 class CompositableClient;
 
 /**
  * A ShadowableLayer is a Layer can be shared with a parent context
  * through a ShadowLayerForwarder.  A ShadowableLayer maps to a
  * Shadow*Layer in a parent context.
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -31,18 +31,17 @@ SharedPlanarYCbCrImage::SharedPlanarYCbC
 : mCompositable(aCompositable)
 {
   MOZ_COUNT_CTOR(SharedPlanarYCbCrImage);
 }
 
 SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() {
   MOZ_COUNT_DTOR(SharedPlanarYCbCrImage);
 
-  if (mCompositable->GetAsyncID() != 0 &&
-      !InImageBridgeChildThread()) {
+  if (mCompositable->GetAsyncHandle() && !InImageBridgeChildThread()) {
     if (mTextureClient) {
       ADDREF_MANUALLY(mTextureClient);
       ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
       mTextureClient = nullptr;
     }
   }
 }
 
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -58,18 +58,17 @@ SharedRGBImage::SharedRGBImage(ImageClie
 {
   MOZ_COUNT_CTOR(SharedRGBImage);
 }
 
 SharedRGBImage::~SharedRGBImage()
 {
   MOZ_COUNT_DTOR(SharedRGBImage);
 
-  if (mCompositable->GetAsyncID() != 0 &&
-      !InImageBridgeChildThread()) {
+  if (mCompositable->GetAsyncHandle() && !InImageBridgeChildThread()) {
     ADDREF_MANUALLY(mTextureClient);
     ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient);
     mTextureClient = nullptr;
   }
 }
 
 bool
 SharedRGBImage::Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -124,17 +124,16 @@ EXPORTS.mozilla.layers += [
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'BSPTree.h',
     'BufferTexture.h',
     'client/CanvasClient.h',
-    'client/CompositableChild.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/GPUVideoTextureClient.h',
     'client/ImageClient.h',
     'client/SingleTiledContentClient.h',
     'client/TextureClient.h',
     'client/TextureClientPool.h',
     'client/TextureClientRecycleAllocator.h',
@@ -289,17 +288,16 @@ UNIFIED_SOURCES += [
     'client/ClientCanvasLayer.cpp',
     'client/ClientColorLayer.cpp',
     'client/ClientContainerLayer.cpp',
     'client/ClientImageLayer.cpp',
     'client/ClientLayerManager.cpp',
     'client/ClientPaintedLayer.cpp',
     'client/ClientTextLayer.cpp',
     'client/ClientTiledPaintedLayer.cpp',
-    'client/CompositableChild.cpp',
     'client/CompositableClient.cpp',
     'client/ContentClient.cpp',
     'client/GPUVideoTextureClient.cpp',
     'client/ImageClient.cpp',
     'client/SingleTiledContentClient.cpp',
     'client/TextureClient.cpp',
     'client/TextureClientPool.cpp',
     'client/TextureClientRecycleAllocator.cpp',
@@ -326,17 +324,16 @@ UNIFIED_SOURCES += [
     'Effects.cpp',
     'FrameMetrics.cpp',
     'GLImages.cpp',
     'ImageDataSerializer.cpp',
     'ImageLayers.cpp',
     'ipc/APZChild.cpp',
     'ipc/APZCTreeManagerChild.cpp',
     'ipc/APZCTreeManagerParent.cpp',
-    'ipc/CompositableForwarder.cpp',
     'ipc/CompositableTransactionParent.cpp',
     'ipc/CompositorBench.cpp',
     'ipc/CompositorBridgeChild.cpp',
     'ipc/CompositorBridgeParent.cpp',
     'ipc/CompositorThread.cpp',
     'ipc/CompositorVsyncScheduler.cpp',
     'ipc/CrossProcessCompositorBridgeParent.cpp',
     'ipc/ImageBridgeChild.cpp',
@@ -397,17 +394,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
         'opengl/MacIOSurfaceTextureHostOGL.cpp',
     ]
 
 IPDL_SOURCES = [
     'ipc/LayersMessages.ipdlh',
     'ipc/LayersSurfaces.ipdlh',
     'ipc/PAPZ.ipdl',
     'ipc/PAPZCTreeManager.ipdl',
-    'ipc/PCompositable.ipdl',
     'ipc/PCompositorBridge.ipdl',
     'ipc/PImageBridge.ipdl',
     'ipc/PLayerTransaction.ipdl',
     'ipc/PTexture.ipdl',
     'ipc/PVideoBridge.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -36,16 +36,22 @@ typedef enum FT_LcdFilter_
 {
     FT_LCD_FILTER_NONE    = 0,
     FT_LCD_FILTER_DEFAULT = 1,
     FT_LCD_FILTER_LIGHT   = 2,
     FT_LCD_FILTER_LEGACY  = 16,
 } FT_LcdFilter;
 #endif
 
+// If compiling with FreeType before 2.5.0
+#ifndef FT_LOAD_COLOR
+#    define FT_LOAD_COLOR ( 1L << 20 )
+#    define FT_PIXEL_MODE_BGRA 7
+#endif
+
 #ifndef SK_CAN_USE_DLOPEN
 #define SK_CAN_USE_DLOPEN 1
 #endif
 #if SK_CAN_USE_DLOPEN
 #include <dlfcn.h>
 #endif
 
 #ifndef SK_FONTHOST_CAIRO_STANDALONE
@@ -391,19 +397,17 @@ SkScalerContext_CairoFT::SkScalerContext
     // advances, as fontconfig and cairo do.
     // See http://code.google.com/p/skia/issues/detail?id=222.
     loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
     if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
         loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
     }
 
-#ifdef FT_LOAD_COLOR
     loadFlags |= FT_LOAD_COLOR;
-#endif
 
     fLoadGlyphFlags = loadFlags;
 }
 
 SkScalerContext_CairoFT::~SkScalerContext_CairoFT()
 {
     cairo_scaled_font_destroy(fScaledFont);
 }
@@ -684,21 +688,19 @@ void SkScalerContext_CairoFT::generateMe
                 glyph->fHeight += 2;
             } else {
                 glyph->fLeft -= 1;
                 glyph->fWidth += 2;
             }
         }
         break;
     case FT_GLYPH_FORMAT_BITMAP:
-#ifdef FT_LOAD_COLOR
         if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
             glyph->fMaskFormat = SkMask::kARGB32_Format;
         }
-#endif
 
         if (isLCD(fRec)) {
             fRec.fMaskFormat = SkMask::kA8_Format;
         }
 
         if (fHaveShape) {
             // Apply the shape matrix to the glyph's bounding box.
             SkMatrix matrix;
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -874,25 +874,18 @@ gfxGDIFontList::MakePlatformFont(const n
     // make a new font entry using the unique name
     WinUserFontData *winUserFontData = new WinUserFontData(fontRef);
     uint16_t w = (aWeight == 0 ? 400 : aWeight);
 
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName,
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
         aStyle, w, aStretch, winUserFontData, false);
 
-    if (!fe)
-        return fe;
-
-    fe->mIsDataUserFont = true;
-
-    // Uniscribe doesn't place CFF fonts loaded privately
-    // via AddFontMemResourceEx on XP/Vista
-    if (isCFF && !IsWin7OrLater()) {
-        fe->mForceGDI = true;
+    if (fe) {
+      fe->mIsDataUserFont = true;
     }
 
     return fe;
 }
 
 bool
 gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily,
                                    nsTArray<gfxFontFamily*>* aOutput,
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -612,16 +612,19 @@ private:
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
+  DECL_GFX_PREF(Live, "webgl.max-perf-warnings",               WebGLMaxPerfWarnings, int32_t, 0);
+  DECL_GFX_PREF(Live, "webgl.max-acceptable-fb-status-invals", WebGLMaxAcceptableFBStatusInvals, int32_t, 0);
+
   DECL_GFX_PREF(Live, "webgl.webgl2-compat-mode",              WebGL2CompatMode, bool, false);
 
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
   // Manage the singleton:
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -188,20 +188,16 @@ public:
         int64_t dedicatedBytesUsed = 0;
         int64_t sharedBytesUsed = 0;
         int64_t committedBytesUsed = 0;
         IDXGIAdapter *DXGIAdapter;
 
         HMODULE gdi32Handle;
         PFND3DKMTQS queryD3DKMTStatistics = nullptr;
 
-        // GPU memory reporting is not available before Windows 7
-        if (!IsWin7OrLater())
-            return NS_OK;
-
         if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
             queryD3DKMTStatistics = (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
 
         if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
             // Most of this block is understood thanks to wj32's work on Process Hacker
 
             DXGI_ADAPTER_DESC adapterDesc;
             D3DKMTQS queryStatistics;
@@ -396,20 +392,16 @@ gfxWindowsPlatform::CanUseHardwareVideoD
     return false;
   }
   return !dm->IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
 }
 
 bool
 gfxWindowsPlatform::InitDWriteSupport()
 {
-  if (!IsVistaOrLater()) {
-    return false;
-  }
-
   // DWrite is only supported on Windows 7 with the platform update and higher.
   // We check this by seeing if D2D1 support is available.
   if (!Factory::SupportsD2D1()) {
     return false;
   }
 
   mozilla::ScopedGfxFeatureReporter reporter("DWrite");
   decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
@@ -1377,27 +1369,23 @@ gfxWindowsPlatform::InitializeD3D9Config
   FeatureState& d3d9 = gfxConfig::GetFeature(Feature::D3D9_COMPOSITING);
 
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
     d3d9.DisableByDefault(FeatureStatus::Unavailable, "Hardware compositing is disabled",
                           NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D9_NEED_HWCOMP"));
     return;
   }
 
-  if (!IsVistaOrLater()) {
-    d3d9.EnableByDefault();
-  } else {
-    d3d9.SetDefaultFromPref(
-      gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
-      true,
-      gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
+  d3d9.SetDefaultFromPref(
+    gfxPrefs::GetLayersAllowD3D9FallbackPrefName(),
+    true,
+    gfxPrefs::GetLayersAllowD3D9FallbackPrefDefault());
 
-    if (!d3d9.IsEnabled() && gfxPrefs::LayersPreferD3D9()) {
-      d3d9.UserEnable("Direct3D9 enabled via layers.prefer-d3d9");
-    }
+  if (!d3d9.IsEnabled() && gfxPrefs::LayersPreferD3D9()) {
+    d3d9.UserEnable("Direct3D9 enabled via layers.prefer-d3d9");
   }
 
   nsCString message;
   nsCString failureId;
   if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &message,
                            failureId)) {
     d3d9.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
   }
@@ -1535,21 +1523,16 @@ gfxWindowsPlatform::InitializeD2DConfig(
 {
   FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
 
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D requires Direct3D 11 compositing",
                           NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
     return;
   }
-  if (!IsVistaOrLater()) {
-    d2d1.DisableByDefault(FeatureStatus::Unavailable, "Direct2D is not available on Windows XP",
-                          NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_XP"));
-    return;
-  }
 
   d2d1.SetDefaultFromPref(
     gfxPrefs::GetDirect2DDisabledPrefName(),
     false,
     gfxPrefs::GetDirect2DDisabledPrefDefault());
 
   nsCString message;
   nsCString failureId;
@@ -1631,24 +1614,22 @@ gfxWindowsPlatform::InitGPUProcessSuppor
 
   if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
     // Don't use the GPU process if not using D3D11.
     gpuProc.Disable(
       FeatureStatus::Unavailable,
       "Not using GPU Process since D3D11 is unavailable",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_D3D11"));
   } else if (!IsWin7SP1OrLater()) {
-    // For Windows XP, we simply don't care enough to support this
-    // configuration. On Windows Vista and 7 Pre-SP1, DXGI 1.2 is not
-    // available and remote presentation for D3D11 will not work. Rather
-    // than take a regression and use D3D9, we revert back to in-process
-    // rendering.
+    // On Windows 7 Pre-SP1, DXGI 1.2 is not available and remote presentation
+    // for D3D11 will not work. Rather than take a regression and use D3D9, we
+    // revert back to in-process rendering.
     gpuProc.Disable(
       FeatureStatus::Unavailable,
-      "Windows XP, Vista, and 7 Pre-SP1 cannot use the GPU process",
+      "Windows 7 Pre-SP1 cannot use the GPU process",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_OLD_WINDOWS"));
   } else if (!IsWin8OrLater()) {
     // Windows 7 SP1 can have DXGI 1.2 only via the Platform Update, so we
     // explicitly check for that here.
     if (!DeviceManagerDx::Get()->CheckRemotePresentSupport()) {
       gpuProc.Disable(
         FeatureStatus::Unavailable,
         "GPU Process requires the Windows 7 Platform Update",
@@ -1661,20 +1642,16 @@ gfxWindowsPlatform::InitGPUProcessSuppor
 
   // If we're still enabled at this point, the user set the force-enabled pref.
   return gpuProc.IsEnabled();
 }
 
 bool
 gfxWindowsPlatform::DwmCompositionEnabled()
 {
-  if (!IsVistaOrLater()) {
-    return false;
-  }
-
   MOZ_ASSERT(WinUtils::dwmIsCompositionEnabledPtr);
   BOOL dwmEnabled = false;
 
   if (FAILED(WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled))) {
     return false;
   }
 
   return dwmEnabled;
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -2262,36 +2262,16 @@ function getDateTimeFormatInternals(obj,
         return internalProps;
 
     // Otherwise it's time to fully create them.
     internalProps = resolveDateTimeFormatInternals(internals.lazyData);
     setInternalProperties(internals, internalProps);
     return internalProps;
 }
 
-/**
- * Components of date and time formats and their values.
- *
- * Spec: ECMAScript Internationalization API Specification, 12.1.1.
- */
-var dateTimeComponentValues = {
-    weekday: ["narrow", "short", "long"],
-    era: ["narrow", "short", "long"],
-    year: ["2-digit", "numeric"],
-    month: ["2-digit", "numeric", "narrow", "short", "long"],
-    day: ["2-digit", "numeric"],
-    hour: ["2-digit", "numeric"],
-    minute: ["2-digit", "numeric"],
-    second: ["2-digit", "numeric"],
-    timeZoneName: ["short", "long"]
-};
-
-
-var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues);
-
 
 /**
  * Initializes an object as a DateTimeFormat.
  *
  * This method is complicated a moderate bit by its implementing initialization
  * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  * all the work we can until the object is actually used as a DateTimeFormat.
  * This later work occurs in |resolveDateTimeFormatInternals|; steps not noted
@@ -2374,22 +2354,29 @@ function InitializeDateTimeFormat(dateTi
     }
     lazyDateTimeFormatData.timeZone = tz;
 
     // Step 18.
     var formatOpt = new Record();
     lazyDateTimeFormatData.formatOpt = formatOpt;
 
     // Step 19.
-    var i, prop;
-    for (i = 0; i < dateTimeComponents.length; i++) {
-        prop = dateTimeComponents[i];
-        var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined);
-        formatOpt[prop] = value;
-    }
+    // 12.1, Table 4: Components of date and time formats.
+    formatOpt.weekday = GetOption(options, "weekday", "string", ["narrow", "short", "long"],
+                                  undefined);
+    formatOpt.era = GetOption(options, "era", "string", ["narrow", "short", "long"], undefined);
+    formatOpt.year = GetOption(options, "year", "string", ["2-digit", "numeric"], undefined);
+    formatOpt.month = GetOption(options, "month", "string",
+                                ["2-digit", "numeric", "narrow", "short", "long"], undefined);
+    formatOpt.day = GetOption(options, "day", "string", ["2-digit", "numeric"], undefined);
+    formatOpt.hour = GetOption(options, "hour", "string", ["2-digit", "numeric"], undefined);
+    formatOpt.minute = GetOption(options, "minute", "string", ["2-digit", "numeric"], undefined);
+    formatOpt.second = GetOption(options, "second", "string", ["2-digit", "numeric"], undefined);
+    formatOpt.timeZoneName = GetOption(options, "timeZoneName", "string", ["short", "long"],
+                                       undefined);
 
     // Steps 20-21 provided by ICU - see comment after this function.
 
     // Step 22.
     //
     // For some reason (ICU not exposing enough interface?) we drop the
     // requested format matcher on the floor after this.  In any case, even if
     // doing so is justified, we have to do this work here in case it triggers
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -74,24 +74,18 @@ HashValue(const Value& v, const mozilla:
     //
     // To avoid revealing GC of atoms, string-based hash codes are computed
     // from the string contents rather than any pointer; to avoid revealing
     // addresses, pointer-based hash codes are computed using the
     // HashCodeScrambler.
 
     if (v.isString())
         return v.toString()->asAtom().hash();
-    if (v.isSymbol()) {
-        Symbol* sym = v.toSymbol();
-        if (sym->isWellKnownSymbol())
-            return HashNumber(sym->code());
-        if (sym->code() == SymbolCode::InSymbolRegistry)
-            return sym->description()->hash();
-        return hcs.scramble(v.asRawBits());
-    }
+    if (v.isSymbol())
+        return v.toSymbol()->hash();
     if (v.isObject())
         return hcs.scramble(v.asRawBits());
 
     MOZ_ASSERT(!v.isGCThing(), "do not reveal pointers via hash codes");
     return v.asRawBits();
 }
 
 HashNumber
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1172,16 +1172,22 @@ Parser<ParseHandler>::tryDeclareVar(Hand
                 bool annexB33Allowance = declaredKind == DeclarationKind::LexicalFunction &&
                                          kind == DeclarationKind::VarForAnnexBLexicalFunction &&
                                          scope == pc->innermostScope();
 
                 if (!annexB35Allowance && !annexB33Allowance) {
                     *redeclaredKind = Some(declaredKind);
                     return true;
                 }
+            } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) {
+                MOZ_ASSERT(DeclarationKindIsParameter(declaredKind));
+
+                // Annex B.3.3.1 disallows redeclaring parameter names.
+                *redeclaredKind = Some(declaredKind);
+                return true;
             }
         } else {
             if (!scope->addDeclaredName(pc, p, name, kind))
                 return false;
         }
     }
 
     if (!pc->sc()->strict() && pc->sc()->isEvalContext())
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -28,16 +28,17 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "irregexp/RegExpEngine.h"
 
 #include "irregexp/NativeRegExpMacroAssembler.h"
 #include "irregexp/RegExpCharacters.h"
 #include "irregexp/RegExpMacroAssembler.h"
+#include "jit/ExecutableAllocator.h"
 #include "jit/JitCommon.h"
 
 #include "irregexp/RegExpCharacters-inl.h"
 
 using namespace js;
 using namespace js::irregexp;
 
 using mozilla::ArrayLength;
@@ -1772,17 +1773,20 @@ irregexp::CompilePattern(JSContext* cx, 
         return RegExpCode();
     }
 
     Maybe<jit::JitContext> ctx;
     Maybe<NativeRegExpMacroAssembler> native_assembler;
     Maybe<InterpretedRegExpMacroAssembler> interpreted_assembler;
 
     RegExpMacroAssembler* assembler;
-    if (IsNativeRegExpEnabled(cx) && !force_bytecode) {
+    if (IsNativeRegExpEnabled(cx) &&
+        !force_bytecode &&
+        jit::CanLikelyAllocateMoreExecutableMemory())
+    {
         NativeRegExpMacroAssembler::Mode mode =
             is_ascii ? NativeRegExpMacroAssembler::ASCII
                      : NativeRegExpMacroAssembler::CHAR16;
 
         ctx.emplace(cx, (jit::TempAllocator*) nullptr);
         native_assembler.emplace(&alloc, shared, cx->runtime(), mode, (data->capture_count + 1) * 2);
         assembler = native_assembler.ptr();
     } else {
--- a/js/src/jit-test/tests/asm.js/directives.txt
+++ b/js/src/jit-test/tests/asm.js/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-noasmjs; test-also-wasm-baseline
+|jit-test| test-also-noasmjs
--- a/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js
+++ b/js/src/jit-test/tests/basic/testFunctionStatementAliasLocals.js
@@ -3,14 +3,16 @@ function f1(b) {
     if (b)
         function w() {}
     return w;
 }
 assertEq(typeof f1(true), "function");
 assertEq(f1(false), 3);
 
 function f2(b, w) {
+    // Annex B doesn't apply to functions in blocks with the same name as a
+    // parameter.
     if (b)
         function w() {}
     return w;
 }
-assertEq(typeof f2(true, 3), "function");
+assertEq(typeof f2(true, 3), "number");
 assertEq(f2(false, 3), 3);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -316,22 +316,27 @@ CanEnterBaselineJIT(JSContext* cx, Handl
         return Method_Skipped;
 
     if (script->length() > BaselineScript::MAX_JSSCRIPT_LENGTH)
         return Method_CantCompile;
 
     if (script->nslots() > BaselineScript::MAX_JSSCRIPT_SLOTS)
         return Method_CantCompile;
 
+    if (script->hasBaselineScript())
+        return Method_Compiled;
+
+    // Check this before calling ensureJitCompartmentExists, so we're less
+    // likely to report OOM in JSRuntime::createJitRuntime.
+    if (!CanLikelyAllocateMoreExecutableMemory())
+        return Method_Skipped;
+
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return Method_Error;
 
-    if (script->hasBaselineScript())
-        return Method_Compiled;
-
     // Check script warm-up counter.
     if (script->incWarmUpCounter() <= JitOptions.baselineWarmUpThreshold)
         return Method_Skipped;
 
     // Frames can be marked as debuggee frames independently of its underlying
     // script being a debuggee script, e.g., when performing
     // Debugger.Frame.prototype.eval.
     return BaselineCompile(cx, script, osrFrame && osrFrame->isDebuggee());
--- a/js/src/jit/ExecutableAllocator.cpp
+++ b/js/src/jit/ExecutableAllocator.cpp
@@ -408,19 +408,19 @@ ExecutableAllocator::poisonCode(JSRuntim
         }
         pool->release();
     }
 }
 
 // Limit on the number of bytes of executable memory to prevent JIT spraying
 // attacks.
 #if JS_BITS_PER_WORD == 32
-static const size_t MaxCodeBytesPerProcess = 128 * 1024 * 1024;
+static const size_t MaxCodeBytesPerProcess = 160 * 1024 * 1024;
 #else
-static const size_t MaxCodeBytesPerProcess = 512 * 1024 * 1024;
+static const size_t MaxCodeBytesPerProcess = 640 * 1024 * 1024;
 #endif
 
 static mozilla::Atomic<size_t> allocatedExecutableBytes(0);
 
 bool
 js::jit::AddAllocatedExecutableBytes(size_t bytes)
 {
     MOZ_ASSERT(allocatedExecutableBytes <= MaxCodeBytesPerProcess);
@@ -448,8 +448,18 @@ js::jit::SubAllocatedExecutableBytes(siz
     allocatedExecutableBytes -= bytes;
 }
 
 void
 js::jit::AssertAllocatedExecutableBytesIsZero()
 {
     MOZ_ASSERT(allocatedExecutableBytes == 0);
 }
+
+bool
+js::jit::CanLikelyAllocateMoreExecutableMemory()
+{
+    // Use a 16 MB buffer.
+    static const size_t BufferSize = 16 * 1024 * 1024;
+
+    MOZ_ASSERT(allocatedExecutableBytes <= MaxCodeBytesPerProcess);
+    return allocatedExecutableBytes + BufferSize <= MaxCodeBytesPerProcess;
+}
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -348,12 +348,21 @@ extern MOZ_MUST_USE bool
 AddAllocatedExecutableBytes(size_t bytes);
 
 extern void
 SubAllocatedExecutableBytes(size_t bytes);
 
 extern void
 AssertAllocatedExecutableBytesIsZero();
 
+// Returns true if we can allocate a few more MB of executable code without
+// hitting our code limit. This function can be used to stop compiling things
+// that are optional (like Baseline and Ion code) when we're about to reach the
+// limit, so we are less likely to OOM or crash. Note that the limit is
+// per-process, so other threads can also allocate code after we call this
+// function.
+extern bool
+CanLikelyAllocateMoreExecutableMemory();
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_ExecutableAllocator_h */
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2497,16 +2497,21 @@ Compile(JSContext* cx, HandleScript scri
         return status;
     }
 
     bool recompile = false;
     OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc);
     if (optimizationLevel == OptimizationLevel::DontCompile)
         return Method_Skipped;
 
+    if (!CanLikelyAllocateMoreExecutableMemory()) {
+        script->resetWarmUpCounter();
+        return Method_Skipped;
+    }
+
     if (script->hasIonScript()) {
         IonScript* scriptIon = script->ionScript();
         if (!scriptIon->method())
             return Method_CantCompile;
 
         // Don't recompile/overwrite higher optimized code,
         // with a lower optimization level.
         if (optimizationLevel <= scriptIon->optimizationLevel() && !forceRecompile)
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4172,16 +4172,19 @@ jit::AnalyzeNewScriptDefiniteProperties(
     AutoTraceLog logCompile(logger, TraceLogger_IonAnalysis);
 
     Vector<PropertyName*> accessedProperties(cx);
 
     LifoAlloc alloc(TempAllocator::PreferredLifoChunkSize);
     TempAllocator temp(&alloc);
     JitContext jctx(cx, &temp);
 
+    if (!jit::CanLikelyAllocateMoreExecutableMemory())
+        return true;
+
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return false;
 
     if (!script->hasBaselineScript()) {
         MethodStatus status = BaselineCompile(cx, script);
         if (status == Method_Error)
             return false;
         if (status != Method_Compiled)
@@ -4417,16 +4420,19 @@ jit::AnalyzeArgumentsUsage(JSContext* cx
     TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, event);
     AutoTraceLog logCompile(logger, TraceLogger_IonAnalysis);
 
     LifoAlloc alloc(TempAllocator::PreferredLifoChunkSize);
     TempAllocator temp(&alloc);
     JitContext jctx(cx, &temp);
 
+    if (!jit::CanLikelyAllocateMoreExecutableMemory())
+        return true;
+
     if (!cx->compartment()->ensureJitCompartmentExists(cx))
         return false;
 
     MIRGraph graph(&temp);
     InlineScriptTree* inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script);
     if (!inlineScriptTree) {
         ReportOutOfMemory(cx);
         return false;
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -233,24 +233,20 @@ DefaultJitOptions::DefaultJitOptions()
     // Test whether wasm bounds check should always be generated.
     SET_DEFAULT(wasmAlwaysCheckBounds, false);
 
     // Toggles the optimization whereby offsets are folded into loads and not
     // included in the bounds check.
     SET_DEFAULT(wasmFoldOffsets, true);
 
     // Until which wasm bytecode size should we accumulate functions, in order
-    // to compile efficiently on helper threads (see also bug 1320374).
-    SET_DEFAULT(wasmBatchThreshold, 10000);
-
-    // In order to have different batching thresholds for Ion and the wasm
-    // baseline, and since a same batch can contain both Ion and baseline
-    // compiled functions, we make Ion functions weight more by using a scaling
-    // factor.
-    SET_DEFAULT(wasmBatchIonScaleFactor, 9);
+    // to compile efficiently on helper threads. Baseline code compiles much
+    // faster than Ion code so use scaled thresholds (see also bug 1320374).
+    SET_DEFAULT(wasmBatchBaselineThreshold, 10000);
+    SET_DEFAULT(wasmBatchIonThreshold, 1100);
 
     // Determines whether we suppress using signal handlers
     // for interrupting jit-ed code. This is used only for testing.
     SET_DEFAULT(ionInterruptWithoutSignals, false);
 }
 
 bool
 DefaultJitOptions::isSmallFunction(JSScript* script) const
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -82,18 +82,18 @@ struct DefaultJitOptions
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t jumpThreshold;
     uint32_t branchPruningHitCountFactor;
     uint32_t branchPruningInstFactor;
     uint32_t branchPruningBlockSpanFactor;
     uint32_t branchPruningEffectfulInstFactor;
     uint32_t branchPruningThreshold;
-    uint32_t wasmBatchThreshold;
-    uint32_t wasmBatchIonScaleFactor;
+    uint32_t wasmBatchIonThreshold;
+    uint32_t wasmBatchBaselineThreshold;
     mozilla::Maybe<uint32_t> forcedDefaultIonWarmUpThreshold;
     mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
     mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
 
     // The options below affect the rest of the VM, and not just the JIT.
     bool disableUnboxedObjects;
 
     DefaultJitOptions();
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1890,29 +1890,21 @@ js::array_sort(JSContext* cx, unsigned a
     if (comp == Match_Failure)
         return false;
 
     if (!fval.isNull() && comp == Match_None) {
         /*
          * Non-optimized user supplied comparators perform much better when
          * called from within a self-hosted sorting function.
          */
-        RootedAtom selfHostedSortAtom(cx, Atomize(cx, "ArraySort", 9));
-        RootedPropertyName selfHostedSortName(cx, selfHostedSortAtom->asPropertyName());
-        RootedValue selfHostedSortValue(cx);
-
-        if (!GlobalObject::getIntrinsicValue(cx, cx->global(), selfHostedSortName,
-            &selfHostedSortValue)) {
-            return false;
-        }
-
-        MOZ_ASSERT(selfHostedSortValue.isObject());
-        MOZ_ASSERT(selfHostedSortValue.toObject().is<JSFunction>());
-
-        return Call(cx, selfHostedSortValue, args.thisv(), fval, args.rval());
+        FixedInvokeArgs<1> args2(cx);
+        args2[0].set(fval);
+
+        RootedValue thisv(cx, ObjectValue(*obj));
+        return CallSelfHostedFunction(cx, cx->names().ArraySort, thisv, args2, args.rval());
     }
 
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
     if (len < 2) {
         /* [] and [a] remain unchanged when sorted. */
         args.rval().setObject(*obj);
@@ -1927,25 +1919,16 @@ js::array_sort(JSContext* cx, unsigned a
      */
 #if JS_BITS_PER_WORD == 32
     if (size_t(len) > size_t(-1) / (2 * sizeof(Value))) {
         ReportAllocationOverflow(cx);
         return false;
     }
 #endif
 
-    /*
-     * Initialize vec as a root. We will clear elements of vec one by
-     * one while increasing the rooted amount of vec when we know that the
-     * property at the corresponding index exists and its value must be rooted.
-     *
-     * In this way when sorting a huge mostly sparse array we will not
-     * access the tail of vec corresponding to properties that do not
-     * exist, allowing OS to avoiding committing RAM. See bug 330812.
-     */
     size_t n, undefs;
     {
         Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx));
         if (!vec.reserve(2 * size_t(len)))
             return false;
 
         /*
          * By ECMA 262, 15.4.4.11, a property that does not exist (which we
@@ -1958,32 +1941,30 @@ js::array_sort(JSContext* cx, unsigned a
         undefs = 0;
         bool allStrings = true;
         bool allInts = true;
         RootedValue v(cx);
         for (uint32_t i = 0; i < len; i++) {
             if (!CheckForInterrupt(cx))
                 return false;
 
-            /* Clear vec[newlen] before including it in the rooted set. */
             bool hole;
             if (!GetElement(cx, obj, i, &hole, &v))
                 return false;
             if (hole)
                 continue;
             if (v.isUndefined()) {
                 ++undefs;
                 continue;
             }
             vec.infallibleAppend(v);
             allStrings = allStrings && v.isString();
             allInts = allInts && v.isInt32();
         }
 
-
         /*
          * If the array only contains holes, we're done.  But if it contains
          * undefs, those must be sorted to the front of the array.
          */
         n = vec.length();
         if (n == 0 && undefs == 0) {
             args.rval().setObject(*obj);
             return true;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -9,16 +9,17 @@
 #ifndef jscntxt_h
 #define jscntxt_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "js/CharacterEncoding.h"
 #include "js/GCVector.h"
 #include "js/Result.h"
+#include "js/Utility.h"
 #include "js/Vector.h"
 #include "vm/Caches.h"
 #include "vm/Runtime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -160,16 +160,22 @@ jit::JitRuntime*
 JSRuntime::createJitRuntime(JSContext* cx)
 {
     // The shared stubs are created in the atoms compartment, which may be
     // accessed by other threads with an exclusive context.
     AutoLockForExclusiveAccess atomsLock(cx);
 
     MOZ_ASSERT(!jitRuntime_);
 
+    if (!CanLikelyAllocateMoreExecutableMemory()) {
+        // Report OOM instead of potentially hitting the MOZ_CRASH below.
+        ReportOutOfMemory(cx);
+        return nullptr;
+    }
+
     jit::JitRuntime* jrt = cx->new_<jit::JitRuntime>(cx->runtime());
     if (!jrt)
         return nullptr;
 
     // Protect jitRuntime_ from being observed (by InterruptRunningJitCode)
     // while it is being initialized. Unfortunately, initialization depends on
     // jitRuntime_ being non-null, so we can't just wait to assign jitRuntime_.
     JitRuntime::AutoPreventBackedgePatching apbp(cx->runtime(), jrt);
@@ -1283,16 +1289,23 @@ JSCompartment::addTelemetry(const char* 
     if (isSystem_)
         return;
     if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
         return;
 
     sawDeprecatedLanguageExtension[e] = true;
 }
 
+HashNumber
+JSCompartment::randomHashCode()
+{
+    ensureRandomNumberGenerator();
+    return HashNumber(randomNumberGenerator.ref().next());
+}
+
 mozilla::HashCodeScrambler
 JSCompartment::randomHashCodeScrambler()
 {
     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                       randomKeyGenerator_.next());
 }
 
 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(ExclusiveContext* ecx
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -701,16 +701,18 @@ struct JSCompartment
 
     // Initialize randomNumberGenerator if needed.
     void ensureRandomNumberGenerator();
 
   private:
     mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
 
   public:
+    js::HashNumber randomHashCode();
+
     mozilla::HashCodeScrambler randomHashCodeScrambler();
 
     static size_t offsetOfRegExps() {
         return offsetof(JSCompartment, regExps);
     }
 
   private:
     JSCompartment* thisForCtor() { return this; }
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1434,8 +1434,20 @@ js::IsWindowProxy(JSObject* obj)
     return obj->getClass() == obj->runtimeFromAnyThread()->maybeWindowProxyClass();
 }
 
 JS_FRIEND_API(bool)
 js::detail::IsWindowSlow(JSObject* obj)
 {
     return obj->as<GlobalObject>().maybeWindowProxy();
 }
+
+AutoAssertNoContentJS::AutoAssertNoContentJS(JSContext* cx)
+  : context_(cx),
+    prevAllowContentJS_(cx->runtime()->allowContentJS_)
+{
+    cx->runtime()->allowContentJS_ = false;
+}
+
+AutoAssertNoContentJS::~AutoAssertNoContentJS()
+{
+    context_->runtime()->allowContentJS_ = prevAllowContentJS_;
+}
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2860,16 +2860,27 @@ extern JS_FRIEND_API(JSObject*)
 ToWindowIfWindowProxy(JSObject* obj);
 
 // Create and add the Intl.PluralRules constructor function to the provided
 // object.  This function throws if called more than once per realm/global
 // object.
 extern bool
 AddPluralRulesConstructor(JSContext* cx, JS::Handle<JSObject*> intl);
 
+class MOZ_STACK_CLASS JS_FRIEND_API(AutoAssertNoContentJS)
+{
+  public:
+    explicit AutoAssertNoContentJS(JSContext* cx);
+    ~AutoAssertNoContentJS();
+
+  private:
+    JSContext* context_;
+    bool prevAllowContentJS_;
+};
+
 } /* namespace js */
 
 class NativeProfiler
 {
   public:
     virtual ~NativeProfiler() {};
     virtual void sampleNative(void* addr, uint32_t size) = 0;
     virtual void removeNative(void* addr) = 0;
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -6,16 +6,17 @@
 
 #include "jswatchpoint.h"
 
 #include "jsatom.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 
 #include "gc/Marking.h"
+#include "vm/Shape.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 inline HashNumber
 WatchKeyHasher::hash(const Lookup& key)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-parameter.js
@@ -0,0 +1,16 @@
+// Annex B.3.3.1 disallows Annex B lexical function behavior when redeclaring a
+// parameter.
+
+(function(f) {
+  if (true) function f() {  }
+  assertEq(f, 123);
+}(123));
+
+(function(f) {
+  { function f() {  } }
+  assertEq(f, 123);
+}(123));
+
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/tests/js1_8/regress/regress-467495-05.js
+++ b/js/src/tests/js1_8/regress/regress-467495-05.js
@@ -15,17 +15,17 @@ test();
 //-----------------------------------------------------------------------------
 
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
-  expect = 'function x() {}';
+  expect = '1';
 
   function g(x) { if (1) function x() {} return x; }
   print(actual = g(1) + '');
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_8/regress/regress-467495-06.js
+++ b/js/src/tests/js1_8/regress/regress-467495-06.js
@@ -27,17 +27,17 @@ function test()
       function x() { }
     if (Math)
       function y() { }
     return [x, y];
   }
 
   var r = f(0);
 
-  if (typeof(r[0]) != "function")
+  if (typeof(r[0]) != "number")
     actual += "Bad r[0]";
 
   if (typeof(r[1]) != "function")
     throw "Bad r[1]";
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -133,17 +133,17 @@ class JitTest:
         t.jitflags = self.jitflags[:]
         t.slow = self.slow
         t.allow_oom = self.allow_oom
         t.allow_unhandlable_oom = self.allow_unhandlable_oom
         t.allow_overrecursed = self.allow_overrecursed
         t.valgrind = self.valgrind
         t.tz_pacific = self.tz_pacific
         t.test_also_noasmjs = self.test_also_noasmjs
-        t.test_also_wasm_baseline = self.test_also_noasmjs
+        t.test_also_wasm_baseline = self.test_also_wasm_baseline
         t.test_also = self.test_also
         t.test_join = self.test_join
         t.expect_error = self.expect_error
         t.expect_status = self.expect_status
         t.expect_crash = self.expect_crash
         t.test_reflect_stringify = self.test_reflect_stringify
         t.enable = True
         t.is_module = self.is_module
new file mode 100644
--- /dev/null
+++ b/js/src/tests/test262/built-ins/ArrayBuffer/data-allocation-after-object-creation.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+es6id: 24.1.2.1
+description: >
+  The new ArrayBuffer instance is created prior to allocating the Data Block.
+info: >
+  ArrayBuffer( length )
+
+  ...
+  6. Return AllocateArrayBuffer(NewTarget, byteLength).
+
+  AllocateArrayBuffer( constructor, byteLength )
+    1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
+       «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
+    2. ReturnIfAbrupt(obj).
+    ...
+    4. Let block be CreateByteDataBlock(byteLength).
+    5. ReturnIfAbrupt(block).
+    ...
+features: [Reflect.construct]
+---*/
+
+function DummyError() { }
+
+var newTarget = function(){}.bind(null);
+Object.defineProperty(newTarget, "prototype", {
+  get: function() {
+    throw new DummyError();
+  }
+});
+
+assert.throws(DummyError, function() {
+  // Allocating 7 PiB should fail with a RangeError.
+  // Math.pow(1024, 5) = 1125899906842624
+  Reflect.construct(ArrayBuffer, [7 * 1125899906842624], newTarget);
+});
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -275,28 +275,31 @@ ArrayBufferObject::class_constructor(JSC
     if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer"))
         return false;
 
     // Step 2.
     uint64_t byteLength;
     if (!ToIndex(cx, args.get(0), &byteLength))
         return false;
 
-    // Non-standard: Refuse to allocate buffers larger than ~2 GiB.
+    // Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).
+    // 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
+    RootedObject proto(cx);
+    RootedObject newTarget(cx, &args.newTarget().toObject());
+    if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
+        return false;
+
+    // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
+    // Refuse to allocate too large buffers, currently limited to ~2 GiB.
     if (byteLength > INT32_MAX) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
         return false;
     }
 
-    // Step 3.
-    RootedObject proto(cx);
-    RootedObject newTarget(cx, &args.newTarget().toObject());
-    if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
-        return false;
-
+    // 24.1.1.1, steps 1 and 4-6.
     JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
     if (!bufobj)
         return false;
     args.rval().setObject(*bufobj);
     return true;
 }
 
 static ArrayBufferObject::BufferContents
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -16,16 +16,17 @@
     macro(allowContentIter, allowContentIter, "allowContentIter") \
     macro(anonymous, anonymous, "anonymous") \
     macro(Any, Any, "Any") \
     macro(apply, apply, "apply") \
     macro(arguments, arguments, "arguments") \
     macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \
     macro(ArrayIterator, ArrayIterator, "Array Iterator") \
     macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
+    macro(ArraySort, ArraySort, "ArraySort") \
     macro(ArraySpecies, ArraySpecies, "ArraySpecies") \
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
     macro(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
     macro(as, as, "as") \
     macro(Async, Async, "Async") \
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -630,17 +630,17 @@ ModuleEnvironmentObject::enumerate(JSCon
 /*****************************************************************************/
 
 const Class WasmFunctionCallObject::class_ = {
     "WasmCall",
     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS)
 };
 
 /* static */ WasmFunctionCallObject*
-WasmFunctionCallObject::createHollowForDebug(JSContext* cx, WasmFunctionScope* scope)
+WasmFunctionCallObject::createHollowForDebug(JSContext* cx, Handle<WasmFunctionScope*> scope)
 {
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     RootedShape shape(cx, scope->getEmptyEnvironmentShape(cx));
     if (!shape)
         return nullptr;
@@ -649,16 +649,17 @@ WasmFunctionCallObject::createHollowForD
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     JSObject* obj;
     JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::DefaultHeap, shape, group));
 
     Rooted<WasmFunctionCallObject*> callobj(cx, &obj->as<WasmFunctionCallObject>());
     callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
+    callobj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope));
 
     return callobj;
 }
 
 /*****************************************************************************/
 
 WithEnvironmentObject*
 WithEnvironmentObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -423,23 +423,29 @@ class ModuleEnvironmentObject : public E
 };
 
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
 
 class WasmFunctionCallObject : public EnvironmentObject
 {
+    // Currently WasmFunctionCallObjects do not use their scopes in a
+    // meaningful way. However, it is an invariant of DebugEnvironments that
+    // environments kept in those maps have live scopes, thus this strong
+    // reference.
+    static const uint32_t SCOPE_SLOT = 1;
+
   public:
     static const Class class_;
 
-    static const uint32_t RESERVED_SLOTS = 1;
+    static const uint32_t RESERVED_SLOTS = 2;
 
     static WasmFunctionCallObject* createHollowForDebug(JSContext* cx,
-                                                        WasmFunctionScope* scope);
+                                                        Handle<WasmFunctionScope*> scope);
 };
 
 class LexicalEnvironmentObject : public EnvironmentObject
 {
     // Global and non-syntactic lexical environments need to store a 'this'
     // value and all other lexical environments have a fixed shape and store a
     // backpointer to the LexicalScope.
     //
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -359,16 +359,19 @@ ExecuteState::pushInterpreterFrame(JSCon
 bool
 js::RunScript(JSContext* cx, RunState& state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     // Since any script can conceivably GC, make sure it's safe to do so.
     cx->runtime()->gc.verifyIsSafeToGC();
 
+    MOZ_DIAGNOSTIC_ASSERT(cx->compartment()->isSystem() ||
+                          cx->runtime()->allowContentJS());
+
     if (!Debugger::checkNoExecute(cx, state.script()))
         return false;
 
 #if defined(MOZ_HAVE_RDTSC)
     js::AutoStopwatch stopwatch(cx);
 #endif // defined(MOZ_HAVE_RDTSC)
 
     SPSEntryMarker marker(cx->runtime(), state.script());
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -11,16 +11,17 @@
 #include "jsobj.h"
 
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/StoreBuffer.h"
 #include "gc/Zone.h"
 #include "js/CharacterEncoding.h"
 #include "vm/ArrayObject.h"
+#include "vm/Shape.h"
 #include "vm/TaggedProto.h"
 #include "vm/UnboxedObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -217,16 +217,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 #if !EXPOSE_INTL_API
     thousandsSeparator(0),
     decimalSeparator(0),
     numGrouping(0),
 #endif
     keepAtoms_(0),
     trustedPrincipals_(nullptr),
     beingDestroyed_(false),
+    allowContentJS_(true),
     atoms_(nullptr),
     atomsCompartment_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -57,16 +57,17 @@
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 namespace js {
 
 class PerThreadData;
 class ExclusiveContext;
+class AutoAssertNoContentJS;
 class AutoKeepAtoms;
 class EnterDebuggeeNoExecute;
 #ifdef JS_TRACE_LOGGING
 class TraceLoggerThread;
 #endif
 
 typedef Vector<UniquePtr<PromiseTask>, 0, SystemAllocPolicy> PromiseTaskPtrVector;
 
@@ -1038,16 +1039,25 @@ struct JSRuntime : public JS::shadow::Ru
   private:
     bool beingDestroyed_;
   public:
     bool isBeingDestroyed() const {
         return beingDestroyed_;
     }
 
   private:
+    bool allowContentJS_;
+  public:
+    bool allowContentJS() const {
+        return allowContentJS_;
+    }
+
+    friend class js::AutoAssertNoContentJS;
+
+  private:
     // Set of all atoms other than those in permanentAtoms and staticStrings.
     // Reading or writing this set requires the calling thread to have an
     // ExclusiveContext and hold a lock. Use AutoLockForExclusiveAccess.
     js::AtomSet* atoms_;
 
     // Compartment and associated zone containing all atoms in the runtime, as
     // well as runtime wide IonCode stubs. Modifying the contents of this
     // compartment requires the calling thread to have an ExclusiveContext and
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -24,16 +24,18 @@
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "gc/Rooting.h"
 #include "js/HashTable.h"
 #include "js/MemoryMetrics.h"
 #include "js/RootingAPI.h"
 #include "js/UbiNode.h"
 #include "vm/ObjectGroup.h"
+#include "vm/String.h"
+#include "vm/Symbol.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
@@ -558,16 +560,40 @@ struct StackBaseShape : public DefaultHa
             MOZ_ASSERT(!base->isOwned());
         }
     };
 
     static inline HashNumber hash(const Lookup& lookup);
     static inline bool match(ReadBarriered<UnownedBaseShape*> key, const Lookup& lookup);
 };
 
+static MOZ_ALWAYS_INLINE js::HashNumber
+HashId(jsid id)
+{
+    // HashGeneric alone would work, but bits of atom and symbol addresses
+    // could then be recovered from the hash code. See bug 1330769.
+    if (MOZ_LIKELY(JSID_IS_ATOM(id)))
+        return JSID_TO_ATOM(id)->hash();
+    if (JSID_IS_SYMBOL(id))
+        return JSID_TO_SYMBOL(id)->hash();
+    return mozilla::HashGeneric(JSID_BITS(id));
+}
+
+template <>
+struct DefaultHasher<jsid>
+{
+    typedef jsid Lookup;
+    static HashNumber hash(jsid id) {
+        return HashId(id);
+    }
+    static bool match(jsid id1, jsid id2) {
+        return id1 == id2;
+    }
+};
+
 using BaseShapeSet = JS::GCHashSet<ReadBarriered<UnownedBaseShape*>,
                                    StackBaseShape,
                                    SystemAllocPolicy>;
 
 class Shape : public gc::TenuredCell
 {
     friend class ::JSObject;
     friend class ::JSFunction;
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -1295,36 +1295,16 @@ template <js::AllowGC allowGC>
 inline JSFlatString*
 NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ utf8)
 {
     return NewStringCopyUTF8N<allowGC>(cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str())));
 }
 
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
-static MOZ_ALWAYS_INLINE js::HashNumber
-HashId(jsid id)
-{
-    if (MOZ_LIKELY(JSID_IS_ATOM(id)))
-        return JSID_TO_ATOM(id)->hash();
-    return mozilla::HashGeneric(JSID_BITS(id));
-}
-
-template <>
-struct DefaultHasher<jsid>
-{
-    typedef jsid Lookup;
-    static HashNumber hash(jsid id) {
-        return HashId(id);
-    }
-    static bool match(jsid id1, jsid id2) {
-        return id1 == id2;
-    }
-};
-
 } /* namespace js */
 
 // Addon IDs are interned atoms which are never destroyed. This detail is
 // not exposed outside the API.
 class JSAddonId : public JSAtom
 {};
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -15,45 +15,45 @@
 #include "vm/StringBuffer.h"
 
 #include "jscompartmentinlines.h"
 
 using JS::Symbol;
 using namespace js;
 
 Symbol*
-Symbol::newInternal(ExclusiveContext* cx, JS::SymbolCode code, JSAtom* description,
+Symbol::newInternal(ExclusiveContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description,
                     AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(cx->compartment() == cx->atomsCompartment(lock));
 
     // Following js::AtomizeString, we grudgingly forgo last-ditch GC here.
     Symbol* p = Allocate<JS::Symbol, NoGC>(cx);
     if (!p) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
-    return new (p) Symbol(code, description);
+    return new (p) Symbol(code, hash, description);
 }
 
 Symbol*
 Symbol::new_(ExclusiveContext* cx, JS::SymbolCode code, JSString* description)
 {
     JSAtom* atom = nullptr;
     if (description) {
         atom = AtomizeString(cx, description);
         if (!atom)
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
     // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock);
-    return newInternal(cx, code, atom, lock);
+    return newInternal(cx, code, cx->compartment()->randomHashCode(), atom, lock);
 }
 
 Symbol*
 Symbol::for_(js::ExclusiveContext* cx, HandleString description)
 {
     JSAtom* atom = AtomizeString(cx, description);
     if (!atom)
         return nullptr;
@@ -61,17 +61,17 @@ Symbol::for_(js::ExclusiveContext* cx, H
     AutoLockForExclusiveAccess lock(cx);
 
     SymbolRegistry& registry = cx->symbolRegistry(lock);
     SymbolRegistry::AddPtr p = registry.lookupForAdd(atom);
     if (p)
         return *p;
 
     AutoCompartment ac(cx, cx->atomsCompartment(lock), &lock);
-    Symbol* sym = newInternal(cx, SymbolCode::InSymbolRegistry, atom, lock);
+    Symbol* sym = newInternal(cx, SymbolCode::InSymbolRegistry, atom->hash(), atom, lock);
     if (!sym)
         return nullptr;
 
     // p is still valid here because we have held the lock since the
     // lookupForAdd call, and newInternal can't GC.
     if (!registry.add(p, sym)) {
         // SystemAllocPolicy does not report OOM.
         ReportOutOfMemory(cx);
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -11,58 +11,65 @@
 
 #include <stdio.h>
 
 #include "jsalloc.h"
 #include "jsapi.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
-
 #include "js/GCHashTable.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
+#include "js/Utility.h"
+#include "vm/String.h"
 
 namespace js {
 class AutoLockForExclusiveAccess;
 } // namespace js
 
 namespace JS {
 
 class Symbol : public js::gc::TenuredCell
 {
   private:
     SymbolCode code_;
+
+    // Each Symbol gets its own hash code so that we don't have to use
+    // addresses as hash codes (a security hazard).
+    js::HashNumber hash_;
+
     JSAtom* description_;
 
     // The minimum allocation size is sizeof(JSString): 16 bytes on 32-bit
-    // architectures and 24 bytes on 64-bit.  8 bytes of padding makes Symbol
+    // architectures and 24 bytes on 64-bit.  A size_t of padding makes Symbol
     // the minimum size on both.
-    uint64_t unused2_;
+    size_t unused_;
 
-    Symbol(SymbolCode code, JSAtom* desc)
-        : code_(code), description_(desc)
+    Symbol(SymbolCode code, js::HashNumber hash, JSAtom* desc)
+        : code_(code), hash_(hash), description_(desc)
     {
-        // Silence warnings about unused2 being... unused.
-        (void)unused2_;
+        // Silence warnings about unused_ being... unused.
+        (void)unused_;
     }
 
     Symbol(const Symbol&) = delete;
     void operator=(const Symbol&) = delete;
 
     static Symbol*
-    newInternal(js::ExclusiveContext* cx, SymbolCode code, JSAtom* description,
-                js::AutoLockForExclusiveAccess& lock);
+    newInternal(js::ExclusiveContext* cx, SymbolCode code, js::HashNumber hash,
+                JSAtom* description, js::AutoLockForExclusiveAccess& lock);
 
   public:
     static Symbol* new_(js::ExclusiveContext* cx, SymbolCode code, JSString* description);
     static Symbol* for_(js::ExclusiveContext* cx, js::HandleString description);
 
     JSAtom* description() const { return description_; }
     SymbolCode code() const { return code_; }
+    js::HashNumber hash() const { return hash_; }
 
     bool isWellKnownSymbol() const { return uint32_t(code_) < WellKnownSymbolLimit; }
 
     static const JS::TraceKind TraceKind = JS::TraceKind::Symbol;
     inline void traceChildren(JSTracer* trc) {
         if (description_)
             js::TraceManuallyBarrieredEdge(trc, &description_, "description");
     }
@@ -88,17 +95,17 @@ namespace js {
 
 /* Hash policy used by the SymbolRegistry. */
 struct HashSymbolsByDescription
 {
     typedef JS::Symbol* Key;
     typedef JSAtom* Lookup;
 
     static HashNumber hash(Lookup l) {
-        return HashNumber(reinterpret_cast<uintptr_t>(l));
+        return HashNumber(l->hash());
     }
     static bool match(Key sym, Lookup l) {
         return sym->description() == l;
     }
 };
 
 /*
  * The runtime-wide symbol registry, used to implement Symbol.for().
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -2,16 +2,17 @@
  * 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 "vm/UnboxedObject-inl.h"
 
 #include "jit/BaselineIC.h"
+#include "jit/ExecutableAllocator.h"
 #include "jit/JitCommon.h"
 #include "jit/Linker.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "vm/Shape-inl.h"
@@ -701,17 +702,18 @@ UnboxedPlainObject::createWithProperties
         if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
             return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
     }
 
 #ifndef JS_CODEGEN_NONE
     if (cx->isJSContext() &&
         !group->unknownProperties() &&
         !layout.constructorCode() &&
-        cx->asJSContext()->runtime()->jitSupportsFloatingPoint)
+        cx->asJSContext()->runtime()->jitSupportsFloatingPoint &&
+        jit::CanLikelyAllocateMoreExecutableMemory())
     {
         if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
             return nullptr;
     }
 #endif
 
     return obj;
 }
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -1711,16 +1711,32 @@ class BaseCompiler
         Stk& v = stk_.back();
         if (v.kind() != Stk::ConstI64)
             return false;
         c = v.i64val();
         stk_.popBack();
         return true;
     }
 
+    MOZ_MUST_USE bool peekConstI32(int32_t* c) {
+        Stk& v = stk_.back();
+        if (v.kind() != Stk::ConstI32)
+            return false;
+        *c = v.i32val();
+        return true;
+    }
+
+    MOZ_MUST_USE bool peekConstI64(int64_t* c) {
+        Stk& v = stk_.back();
+        if (v.kind() != Stk::ConstI64)
+            return false;
+        *c = v.i64val();
+        return true;
+    }
+
     MOZ_MUST_USE bool popConstPositivePowerOfTwoI32(int32_t& c,
                                                     uint_fast8_t& power,
                                                     int32_t cutoff)
     {
         Stk& v = stk_.back();
         if (v.kind() != Stk::ConstI32)
             return false;
         c = v.i32val();
@@ -2753,22 +2769,25 @@ class BaseCompiler
             masm.jump(done);
         } else {
             masm.jump(trap(Trap::IntegerOverflow));
         }
         masm.bind(&notmin);
     }
 
 #ifndef INT_DIV_I64_CALLOUT
-    void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned) {
+    void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
+                     bool isConst, int64_t c)
+    {
         Label done;
 
-        checkDivideByZeroI64(rhs);
-
-        if (!isUnsigned)
+        if (!isConst || c == 0)
+            checkDivideByZeroI64(rhs);
+
+        if (!isUnsigned && (!isConst || c == -1))
             checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
 
 # if defined(JS_CODEGEN_X64)
         // The caller must set up the following situation.
         MOZ_ASSERT(srcDest.reg == rax);
         MOZ_ASSERT(isAvailable(rdx));
         if (isUnsigned) {
             masm.xorq(rdx, rdx);
@@ -2778,22 +2797,25 @@ class BaseCompiler
             masm.idivq(rhs.reg);
         }
 # else
         MOZ_CRASH("BaseCompiler platform hook: quotientI64");
 # endif
         masm.bind(&done);
     }
 
-    void remainderI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned) {
+    void remainderI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
+                      bool isConst, int64_t c)
+    {
         Label done;
 
-        checkDivideByZeroI64(rhs);
-
-        if (!isUnsigned)
+        if (!isConst || c == 0)
+            checkDivideByZeroI64(rhs);
+
+        if (!isUnsigned && (!isConst || c == -1))
             checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
 
 # if defined(JS_CODEGEN_X64)
         // The caller must set up the following situation.
         MOZ_ASSERT(srcDest.reg == rax);
         MOZ_ASSERT(isAvailable(rdx));
 
         if (isUnsigned) {
@@ -4179,22 +4201,25 @@ BaseCompiler::emitQuotientI32()
             masm.branchTest32(Assembler::NotSigned, r, r, &positive);
             masm.add32(Imm32(c-1), r);
             masm.bind(&positive);
 
             masm.rshift32Arithmetic(Imm32(power & 31), r);
             pushI32(r);
         }
     } else {
+        bool isConst = peekConstI32(&c);
         RegI32 r0, r1;
         pop2xI32ForIntMulDiv(&r0, &r1);
 
         Label done;
-        checkDivideByZeroI32(r1, r0, &done);
-        checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(false));
+        if (!isConst || c == 0)
+            checkDivideByZeroI32(r1, r0, &done);
+        if (!isConst || c == -1)
+            checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(false));
         masm.quotient32(r1, r0, IsUnsigned(false));
         masm.bind(&done);
 
         freeI32(r1);
         pushI32(r0);
     }
 }
 
@@ -4205,21 +4230,23 @@ BaseCompiler::emitQuotientU32()
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI32(c, power, 0)) {
         if (power != 0) {
             RegI32 r = popI32();
             masm.rshift32(Imm32(power & 31), r);
             pushI32(r);
         }
     } else {
+        bool isConst = peekConstI32(&c);
         RegI32 r0, r1;
         pop2xI32ForIntMulDiv(&r0, &r1);
 
         Label done;
-        checkDivideByZeroI32(r1, r0, &done);
+        if (!isConst || c == 0)
+            checkDivideByZeroI32(r1, r0, &done);
         masm.quotient32(r1, r0, IsUnsigned(true));
         masm.bind(&done);
 
         freeI32(r1);
         pushI32(r0);
     }
 }
 
@@ -4240,22 +4267,25 @@ BaseCompiler::emitRemainderI32()
 
         masm.rshift32Arithmetic(Imm32(power & 31), temp);
         masm.lshift32(Imm32(power & 31), temp);
         masm.sub32(temp, r);
         freeI32(temp);
 
         pushI32(r);
     } else {
+        bool isConst = peekConstI32(&c);
         RegI32 r0, r1;
         pop2xI32ForIntMulDiv(&r0, &r1);
 
         Label done;
-        checkDivideByZeroI32(r1, r0, &done);
-        checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(true));
+        if (!isConst || c == 0)
+            checkDivideByZeroI32(r1, r0, &done);
+        if (!isConst || c == -1)
+            checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(true));
         masm.remainder32(r1, r0, IsUnsigned(false));
         masm.bind(&done);
 
         freeI32(r1);
         pushI32(r0);
     }
 }
 
@@ -4264,21 +4294,23 @@ BaseCompiler::emitRemainderU32()
 {
     int32_t c;
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI32(c, power, 1)) {
         RegI32 r = popI32();
         masm.and32(Imm32(c-1), r);
         pushI32(r);
     } else {
+        bool isConst = peekConstI32(&c);
         RegI32 r0, r1;
         pop2xI32ForIntMulDiv(&r0, &r1);
 
         Label done;
-        checkDivideByZeroI32(r1, r0, &done);
+        if (!isConst || c == 0)
+            checkDivideByZeroI32(r1, r0, &done);
         masm.remainder32(r1, r0, IsUnsigned(true));
         masm.bind(&done);
 
         freeI32(r1);
         pushI32(r0);
     }
 }
 
@@ -4297,19 +4329,20 @@ BaseCompiler::emitQuotientI64()
                               &positive);
             masm.add64(Imm32(c-1), r);
             masm.bind(&positive);
 
             masm.rshift64Arithmetic(Imm32(power & 63), r);
             pushI64(r);
         }
     } else {
+        bool isConst = peekConstI64(&c);
         RegI64 r0, r1;
         pop2xI64ForIntDiv(&r0, &r1);
-        quotientI64(r1, r0, IsUnsigned(false));
+        quotientI64(r1, r0, IsUnsigned(false), isConst, c);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
 # endif
 }
 
@@ -4321,19 +4354,20 @@ BaseCompiler::emitQuotientU64()
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI64(c, power, 0)) {
         if (power != 0) {
             RegI64 r = popI64();
             masm.rshift64(Imm32(power & 63), r);
             pushI64(r);
         }
     } else {
+        bool isConst = peekConstI64(&c);
         RegI64 r0, r1;
         pop2xI64ForIntDiv(&r0, &r1);
-        quotientI64(r1, r0, IsUnsigned(true));
+        quotientI64(r1, r0, IsUnsigned(true), isConst, c);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
 # endif
 }
 
@@ -4356,19 +4390,20 @@ BaseCompiler::emitRemainderI64()
 
         masm.rshift64Arithmetic(Imm32(power & 63), temp);
         masm.lshift64(Imm32(power & 63), temp);
         masm.sub64(temp, r);
         freeI64(temp);
 
         pushI64(r);
     } else {
+        bool isConst = peekConstI64(&c);
         RegI64 r0, r1;
         pop2xI64ForIntDiv(&r0, &r1);
-        remainderI64(r1, r0, IsUnsigned(false));
+        remainderI64(r1, r0, IsUnsigned(false), isConst, c);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
 # endif
 }
 
@@ -4378,19 +4413,20 @@ BaseCompiler::emitRemainderU64()
 # ifdef JS_PUNBOX64
     int64_t c;
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI64(c, power, 1)) {
         RegI64 r = popI64();
         masm.and64(Imm64(c-1), r);
         pushI64(r);
     } else {
+        bool isConst = peekConstI64(&c);
         RegI64 r0, r1;
         pop2xI64ForIntDiv(&r0, &r1);
-        remainderI64(r1, r0, IsUnsigned(true));
+        remainderI64(r1, r0, IsUnsigned(true), isConst, c);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
 # endif
 }
 #endif // INT_DIV_I64_CALLOUT
@@ -7861,17 +7897,17 @@ volatileReturnGPR()
 }
 
 LiveRegisterSet BaseCompiler::VolatileReturnGPR = volatileReturnGPR();
 
 } // wasm
 } // js
 
 bool
-js::wasm::BaselineCanCompile(const FunctionGenerator* fg)
+js::wasm::BaselineCanCompile()
 {
     // On all platforms we require signals for AsmJS/Wasm.
     // If we made it this far we must have signals.
     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
 
 #if defined(JS_CODEGEN_ARM)
     // Simplifying assumption: require SDIV and UDIV.
     //
@@ -7879,32 +7915,26 @@ js::wasm::BaselineCanCompile(const Funct
     // X% of devices in the market implement SDIV and UDIV.  However,
     // they are definitely implemented on the Cortex-A7 and Cortex-A15
     // and on all ARMv8 systems.
     if (!HasIDIV())
         return false;
 #endif
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
-    // AsmJS code may use SIMD or atomics, which Baseline doesn't currently
-    // handle. Since we haven't yet validated the function, we don't know
-    // whether it actually uses those features. Assume the worst.
-    if (fg->isAsmJS())
-        return false;
-
     return true;
 #else
     return false;
 #endif
 }
 
 bool
 js::wasm::BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars *error)
 {
-    MOZ_ASSERT(unit->mode() == CompileMode::Baseline);
+    MOZ_ASSERT(task->mode() == CompileMode::Baseline);
 
     const FuncBytes& func = unit->func();
     uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes(), error);
 
     if (!ValidateFunctionBody(task->env(), func.index(), bodySize, d))
         return false;
--- a/js/src/wasm/WasmBaselineCompile.h
+++ b/js/src/wasm/WasmBaselineCompile.h
@@ -19,31 +19,23 @@
 #ifndef asmjs_wasm_baseline_compile_h
 #define asmjs_wasm_baseline_compile_h
 
 #include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
-class FunctionGenerator;
 class CompileTask;
 class FuncCompileUnit;
 
-// Return true if BaselineCompileFunction can generate code for the
-// function held in the FunctionGenerator.  If false is returned a
-// different compilation strategy must be chosen.
-//
-// This allows the baseline compiler to have different capabilities on
-// different platforms and defer to the full Ion compiler if
-// capabilities are missing.  The FunctionGenerator and other data
-// structures contain information about the capabilities that are
-// required to compile the function.
+// Return whether BaselineCompileFunction can generate code on the current device.
+// Note: asm.js is also currently not supported due to Atomics and SIMD.
 bool
-BaselineCanCompile(const FunctionGenerator* fg);
+BaselineCanCompile();
 
 // Generate adequate code quickly.
 bool
 BaselineCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error);
 
 } // namespace wasm
 } // namespace js
 
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -40,18 +40,17 @@ using mozilla::MakeEnumeratedRange;
 // ****************************************************************************
 // ModuleGenerator
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 static const uint32_t BAD_CODE_RANGE = UINT32_MAX;
 
 ModuleGenerator::ModuleGenerator(UniqueChars* error)
-  : alwaysBaseline_(false),
-    debugEnabled_(false),
+  : compileMode_(CompileMode(-1)),
     error_(error),
     numSigs_(0),
     numTables_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     masmAlloc_(&lifo_),
     masm_(MacroAssembler::WasmToken(), masmAlloc_),
     lastPatchedCallsite_(0),
     startOfUnpatchedCallsites_(0),
@@ -107,38 +106,49 @@ ModuleGenerator::~ModuleGenerator()
 bool
 ModuleGenerator::initAsmJS(Metadata* asmJSMetadata)
 {
     MOZ_ASSERT(env_->isAsmJS());
 
     metadata_ = asmJSMetadata;
     MOZ_ASSERT(isAsmJS());
 
+    // Enabling debugging requires baseline and baseline is only enabled for
+    // wasm (since the baseline does not currently support Atomics or SIMD).
+
+    metadata_->debugEnabled = false;
+    compileMode_ = CompileMode::Ion;
+
     // For asm.js, the Vectors in ModuleEnvironment are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated.
 
     MOZ_ASSERT(env_->sigs.length() == AsmJSMaxTypes);
     MOZ_ASSERT(env_->tables.length() == AsmJSMaxTables);
     MOZ_ASSERT(env_->asmJSSigToTableIndex.length() == AsmJSMaxTypes);
 
     return true;
 }
 
 bool
-ModuleGenerator::initWasm()
+ModuleGenerator::initWasm(const CompileArgs& args)
 {
     MOZ_ASSERT(!env_->isAsmJS());
 
     metadata_ = js_new<Metadata>();
     if (!metadata_)
         return false;
 
     MOZ_ASSERT(!isAsmJS());
 
+    metadata_->debugEnabled = args.debugEnabled && BaselineCanCompile();
+    compileMode_ = args.alwaysBaseline || metadata_->debugEnabled
+                   ? CompileMode::Baseline
+                   : CompileMode::Ion;
+
     // For wasm, the Vectors are correctly-sized and already initialized.
 
     numSigs_ = env_->sigs.length();
     numTables_ = env_->tables.length();
 
     for (size_t i = 0; i < env_->funcImportGlobalDataOffsets.length(); i++) {
         env_->funcImportGlobalDataOffsets[i] = linkData_.globalDataLength;
         linkData_.globalDataLength += sizeof(FuncImportTls);
@@ -197,29 +207,26 @@ ModuleGenerator::initWasm()
 bool
 ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args,
                       Metadata* maybeAsmJSMetadata)
 {
     env_ = Move(env);
 
     linkData_.globalDataLength = AlignBytes(InitialGlobalDataBytes, sizeof(void*));
 
-    alwaysBaseline_ = args.alwaysBaseline;
-    debugEnabled_ = args.debugEnabled;
-
     if (!funcToCodeRange_.appendN(BAD_CODE_RANGE, env_->funcSigs.length()))
         return false;
 
     if (!assumptions_.clone(args.assumptions))
         return false;
 
     if (!exportedFuncs_.init())
         return false;
 
-    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm())
+    if (env_->isAsmJS() ? !initAsmJS(maybeAsmJSMetadata) : !initWasm(args))
         return false;
 
     if (args.scriptedCaller.filename) {
         metadata_->filename = DuplicateString(args.scriptedCaller.filename.get());
         if (!metadata_->filename)
             return false;
     }
 
@@ -898,17 +905,17 @@ ModuleGenerator::startFuncDefs()
         numTasks = 2 * threads.maxWasmCompilationThreads();
     } else {
         numTasks = 1;
     }
 
     if (!tasks_.initCapacity(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
-        tasks_.infallibleEmplaceBack(*env_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
+        tasks_.infallibleEmplaceBack(*env_, compileMode_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
 
     if (!freeTasks_.reserve(numTasks))
         return false;
     for (size_t i = 0; i < numTasks; i++)
         freeTasks_.infallibleAppend(&tasks_[i]);
 
     startedFuncDefs_ = true;
     MOZ_ASSERT(!finishedFuncDefs_);
@@ -943,17 +950,17 @@ ModuleGenerator::startFuncDef(uint32_t l
     return true;
 }
 
 bool
 ModuleGenerator::launchBatchCompile()
 {
     MOZ_ASSERT(currentTask_);
 
-    currentTask_->setDebugEnabled(debugEnabled_);
+    currentTask_->setDebugEnabled(metadata_->debugEnabled);
 
     size_t numBatchedFuncs = currentTask_->units().length();
     MOZ_ASSERT(numBatchedFuncs);
 
     if (parallel_) {
         if (!StartOffThreadWasmCompile(currentTask_))
             return false;
         outstanding_++;
@@ -972,40 +979,30 @@ ModuleGenerator::launchBatchCompile()
 }
 
 bool
 ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFuncDef_ == fg);
 
     UniqueFuncBytes func = Move(fg->funcBytes_);
-
     func->setFunc(funcIndex, &funcSig(funcIndex));
+    uint32_t funcBytecodeLength = func->bytes().length();
+    if (!currentTask_->units().emplaceBack(Move(func)))
+        return false;
 
-    CompileMode mode;
-    if ((alwaysBaseline_ || debugEnabled_) && BaselineCanCompile(fg)) {
-      mode = CompileMode::Baseline;
-    } else {
-      mode = CompileMode::Ion;
-      // Ion does not support debugging -- reset debugEnabled_ flags to avoid
-      // turning debugging for wasm::Code.
-      debugEnabled_ = false;
+    uint32_t threshold;
+    switch (compileMode_) {
+      case CompileMode::Baseline: threshold = JitOptions.wasmBatchBaselineThreshold; break;
+      case CompileMode::Ion:      threshold = JitOptions.wasmBatchIonThreshold;      break;
     }
 
-    CheckedInt<uint32_t> newBatched = func->bytes().length();
-    if (mode == CompileMode::Ion)
-        newBatched *= JitOptions.wasmBatchIonScaleFactor;
-    newBatched += batchedBytecode_;
-
-    if (!currentTask_->units().emplaceBack(Move(func), mode))
-        return false;
-
-    if (newBatched.isValid() && newBatched.value() < JitOptions.wasmBatchThreshold)
-        batchedBytecode_ = newBatched.value();
-    else if (!launchBatchCompile())
+    batchedBytecode_ += funcBytecodeLength;
+    MOZ_ASSERT(batchedBytecode_ <= MaxModuleBytes);
+    if (batchedBytecode_ > threshold && !launchBatchCompile())
         return false;
 
     fg->m_ = nullptr;
     activeFuncDef_ = nullptr;
     return true;
 }
 
 bool
@@ -1184,18 +1181,16 @@ ModuleGenerator::finish(const ShareableB
     metadata_->callThunks.podResizeToFit();
     metadata_->debugTrapFarJumpOffsets.podResizeToFit();
 
     // For asm.js, the tables vector is over-allocated (to avoid resize during
     // parallel copilation). Shrink it back down to fit.
     if (isAsmJS() && !metadata_->tables.resize(numTables_))
         return nullptr;
 
-    metadata_->debugEnabled = debugEnabled_;
-
     // Assert CodeRanges are sorted.
 #ifdef DEBUG
     uint32_t lastEnd = 0;
     for (const CodeRange& codeRange : metadata_->codeRanges) {
         MOZ_ASSERT(codeRange.begin() >= lastEnd);
         lastEnd = codeRange.end();
     }
 #endif
@@ -1224,23 +1219,25 @@ ModuleGenerator::finish(const ShareableB
 }
 
 bool
 wasm::CompileFunction(CompileTask* task, UniqueChars* error)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread();
     AutoTraceLog logCompile(logger, TraceLogger_WasmCompilation);
 
-    for (FuncCompileUnit& unit : task->units()) {
-        switch (unit.mode()) {
-          case CompileMode::Ion:
+    switch (task->mode()) {
+      case CompileMode::Ion:
+        for (FuncCompileUnit& unit : task->units()) {
             if (!IonCompileFunction(task, &unit, error))
                 return false;
-            break;
-          case CompileMode::Baseline:
+        }
+        break;
+      case CompileMode::Baseline:
+        for (FuncCompileUnit& unit : task->units()) {
             if (!BaselineCompileFunction(task, &unit, error))
                 return false;
-            break;
         }
+        break;
     }
 
     return true;
 }
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -96,29 +96,26 @@ enum class CompileMode
 };
 
 // FuncCompileUnit contains all the data necessary to produce and store the
 // results of a single function's compilation.
 
 class FuncCompileUnit
 {
     UniqueFuncBytes func_;
-    CompileMode mode_;
     FuncOffsets offsets_;
     DebugOnly<bool> finished_;
 
   public:
-    FuncCompileUnit(UniqueFuncBytes func, CompileMode mode)
+    explicit FuncCompileUnit(UniqueFuncBytes func)
       : func_(Move(func)),
-        mode_(mode),
         finished_(false)
     {}
 
     const FuncBytes& func() const { return *func_; }
-    CompileMode mode() const { return mode_; }
     FuncOffsets offsets() const { MOZ_ASSERT(finished_); return offsets_; }
 
     void finish(FuncOffsets offsets) {
         MOZ_ASSERT(!finished_);
         offsets_ = offsets;
         finished_ = true;
     }
 
@@ -135,34 +132,36 @@ typedef Vector<FuncCompileUnit, 8, Syste
 // filled with a certain number of function's bodies that are sent off to a
 // compilation helper thread, which fills in the resulting code offsets, and
 // finally sent back to the validation thread. To save time allocating and
 // freeing memory, CompileTasks are reset() and reused.
 
 class CompileTask
 {
     const ModuleEnvironment&   env_;
+    CompileMode                mode_;
     LifoAlloc                  lifo_;
     Maybe<jit::TempAllocator>  alloc_;
     Maybe<jit::MacroAssembler> masm_;
     FuncCompileUnitVector      units_;
     bool                       debugEnabled_;
 
     CompileTask(const CompileTask&) = delete;
     CompileTask& operator=(const CompileTask&) = delete;
 
     void init() {
         alloc_.emplace(&lifo_);
         masm_.emplace(jit::MacroAssembler::WasmToken(), *alloc_);
         debugEnabled_ = false;
     }
 
   public:
-    CompileTask(const ModuleEnvironment& env, size_t defaultChunkSize)
+    CompileTask(const ModuleEnvironment& env, CompileMode mode, size_t defaultChunkSize)
       : env_(env),
+        mode_(mode),
         lifo_(defaultChunkSize)
     {
         init();
     }
     LifoAlloc& lifo() {
         return lifo_;
     }
     jit::TempAllocator& alloc() {
@@ -172,16 +171,19 @@ class CompileTask
         return env_;
     }
     jit::MacroAssembler& masm() {
         return *masm_;
     }
     FuncCompileUnitVector& units() {
         return units_;
     }
+    CompileMode mode() const {
+        return mode_;
+    }
     bool debugEnabled() const {
         return debugEnabled_;
     }
     void setDebugEnabled(bool enabled) {
         debugEnabled_ = enabled;
     }
     bool reset(UniqueFuncBytesVector* freeFuncBytes) {
         for (FuncCompileUnit& unit : units_) {
@@ -208,18 +210,17 @@ class CompileTask
 class MOZ_STACK_CLASS ModuleGenerator
 {
     typedef HashSet<uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> Uint32Set;
     typedef Vector<CompileTask, 0, SystemAllocPolicy> CompileTaskVector;
     typedef Vector<CompileTask*, 0, SystemAllocPolicy> CompileTaskPtrVector;
     typedef EnumeratedArray<Trap, Trap::Limit, ProfilingOffsets> TrapExitOffsetArray;
 
     // Constant parameters
-    bool                            alwaysBaseline_;
-    bool                            debugEnabled_;
+    CompileMode                     compileMode_;
     UniqueChars*                    error_;
 
     // Data that is moved into the result of finish()
     Assumptions                     assumptions_;
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
 
     // Data scoped to the ModuleGenerator's lifetime
@@ -263,17 +264,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool finishLinkData(Bytes& code);
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
     MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
     MOZ_MUST_USE bool launchBatchCompile();
 
     MOZ_MUST_USE bool initAsmJS(Metadata* asmJSMetadata);
-    MOZ_MUST_USE bool initWasm();
+    MOZ_MUST_USE bool initWasm(const CompileArgs& args);
 
   public:
     explicit ModuleGenerator(UniqueChars* error);
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleEnvironment env, const CompileArgs& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3652,17 +3652,17 @@ EmitExpr(FunctionCompiler& f)
     }
 
     MOZ_CRASH("unexpected wasm opcode");
 }
 
 bool
 wasm::IonCompileFunction(CompileTask* task, FuncCompileUnit* unit, UniqueChars* error)
 {
-    MOZ_ASSERT(unit->mode() == CompileMode::Ion);
+    MOZ_ASSERT(task->mode() == CompileMode::Ion);
 
     const FuncBytes& func = unit->func();
     const ModuleEnvironment& env = task->env();
     uint32_t bodySize = func.bytes().length();
 
     Decoder d(func.bytes(), error);
 
     if (!env.isAsmJS()) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -163,16 +163,17 @@
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "ClientLayerManager.h"
 #include "GeckoProfiler.h"
 #include "gfxPlatform.h"
 #include "Layers.h"
 #include "LayerTreeInvalidation.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/dom/DocumentTimeline.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsCanvasFrame.h"
 #include "nsIImageLoadingContent.h"
 #include "nsImageFrame.h"
 #include "nsIScreen.h"
 #include "nsIScreenManager.h"
 #include "nsPlaceholderFrame.h"
@@ -6234,16 +6235,23 @@ PresShell::RecordShadowStyleChange(Shado
 void
 PresShell::Paint(nsView*        aViewToPaint,
                  const nsRegion& aDirtyRegion,
                  uint32_t        aFlags)
 {
   PROFILER_LABEL("PresShell", "Paint",
     js::ProfileEntry::Category::GRAPHICS);
 
+  Maybe<js::AutoAssertNoContentJS> nojs;
+  if (!(aFlags & nsIPresShell::PAINT_COMPOSITE)) {
+    // We need to allow content JS when the flag is set since we may trigger
+    // MozAfterPaint events in content in those cases.
+    nojs.emplace(dom::danger::GetJSContext());
+  }
+
   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   NS_ASSERTION(aViewToPaint, "null view");
 
   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
 
   if (!mIsActive) {
     return;
   }
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -83,29 +83,19 @@ ServoRestyleManager::PostRestyleEventFor
 {
   PostRestyleEventInternal(true);
 }
 
 void
 ServoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
                                          nsRestyleHint aRestyleHint)
 {
-  NS_WARNING("stylo: ServoRestyleManager::RebuildAllStyleData not implemented");
-  // That said, we do know that rebuilding all style data in Gecko would get rid
-  // of the old ruletree, and hence of the cached-on-the-root default computed
-  // styles.  So we know we need to clear them here.  I think this is the only
-  // way they could get cleared, in fact, though not _all_ calls that come
-  // through here may need to clear them in practice.
-  //
-  // We probably need to do some actual restyling here too, though.  And figure
-  // out whether it actually matters that we may be recomputing the default
-  // styles in too many cases.  For one thing, we do a bunch of eager work here,
-  // whereas we should really just set a bit that says to recompute the default
-  // computed styles before the next time we restyle anything!
-  StyleSet()->RecomputeDefaultComputedStyles();
+  // TODO(emilio, bz): We probably need to do some actual restyling here too.
+  NS_WARNING("stylo: ServoRestyleManager::RebuildAllStyleData is incomplete");
+  StyleSet()->RebuildData();
 }
 
 void
 ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                                   nsRestyleHint aRestyleHint)
 {
   NS_WARNING("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
 }
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -101,74 +101,74 @@ public:
 
   friend nsBlockFrame* NS_NewBlockFrame(nsIPresShell* aPresShell,
                                         nsStyleContext* aContext);
 
   // nsQueryFrame
   NS_DECL_QUERYFRAME
 
   // nsIFrame
-  virtual void Init(nsIContent* aContent,
-                    nsContainerFrame* aParent,
-                    nsIFrame* aPrevInFlow) override;
-  virtual void SetInitialChildList(ChildListID aListID,
-                                   nsFrameList& aChildList) override;
-  virtual void AppendFrames(ChildListID aListID,
-                            nsFrameList& aFrameList) override;
-  virtual void InsertFrames(ChildListID aListID,
-                            nsIFrame* aPrevFrame,
-                            nsFrameList& aFrameList) override;
-  virtual void RemoveFrame(ChildListID aListID,
-                           nsIFrame* aOldFrame) override;
-  virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
-  virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
-  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+  void Init(nsIContent* aContent,
+            nsContainerFrame* aParent,
+            nsIFrame* aPrevInFlow) override;
+  void SetInitialChildList(ChildListID aListID,
+                           nsFrameList& aChildList) override;
+  void AppendFrames(ChildListID aListID,
+                    nsFrameList& aFrameList) override;
+  void InsertFrames(ChildListID aListID,
+                    nsIFrame* aPrevFrame,
+                    nsFrameList& aFrameList) override;
+  void RemoveFrame(ChildListID aListID,
+                   nsIFrame* aOldFrame) override;
+  const nsFrameList& GetChildList(ChildListID aListID) const override;
+  void GetChildLists(nsTArray<ChildList>* aLists) const override;
+  nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
   bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
                                 nscoord* aBaseline) const override
   {
     nscoord lastBaseline;
     if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
       *aBaseline = BSize() - lastBaseline;
       return true;
     }
     return false;
   }
   bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
                                  BaselineSharingGroup aBaselineGroup,
                                  nscoord*             aBaseline) const override;
-  virtual nscoord GetCaretBaseline() const override;
-  virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
-  virtual nsSplittableType GetSplittableType() const override;
-  virtual bool IsFloatContainingBlock() const override;
-  virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
-                                const nsRect& aDirtyRect,
-                                const nsDisplayListSet& aLists) override;
-  virtual nsIAtom* GetType() const override;
-  virtual bool IsFrameOfType(uint32_t aFlags) const override
+  nscoord GetCaretBaseline() const override;
+  void DestroyFrom(nsIFrame* aDestructRoot) override;
+  nsSplittableType GetSplittableType() const override;
+  bool IsFloatContainingBlock() const override;
+  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
+                        const nsRect& aDirtyRect,
+                        const nsDisplayListSet& aLists) override;
+  nsIAtom* GetType() const override;
+  bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsContainerFrame::IsFrameOfType(aFlags &
              ~(nsIFrame::eCanContainOverflowContainers |
                nsIFrame::eBlockFrame));
   }
 
-  virtual void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
-  virtual void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
+  void InvalidateFrame(uint32_t aDisplayItemKey = 0) override;
+  void InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey = 0) override;
 
 #ifdef DEBUG_FRAME_DUMP
   void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const override;
-  virtual nsresult GetFrameName(nsAString& aResult) const override;
+  nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
 #ifdef DEBUG
-  virtual nsFrameState GetDebugStateBits() const override;
+  nsFrameState GetDebugStateBits() const override;
   const char* LineReflowStatusToString(LineReflowStatus aLineReflowStatus) const;
 #endif
 
 #ifdef ACCESSIBILITY
-  virtual mozilla::a11y::AccType AccessibleType() override;
+  mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   // Line cursor methods to speed up line searching in which one query
   // result is expected to be close to the next in general. This is
   // mainly for searching line(s) containing a point. It is also used
   // as a cache for local computation. Use AutoLineCursorSetup for the
   // latter case so that it wouldn't interact unexpectedly with the
   // former. The basic idea for the former is that we set the cursor
@@ -221,22 +221,22 @@ public:
       }
     }
 
   private:
     nsBlockFrame* mFrame;
     nsLineBox* mOrigCursor;
   };
 
-  virtual void ChildIsDirty(nsIFrame* aChild) override;
-  virtual bool IsVisibleInSelection(nsISelection* aSelection) override;
+  void ChildIsDirty(nsIFrame* aChild) override;
+  bool IsVisibleInSelection(nsISelection* aSelection) override;
 
-  virtual bool IsEmpty() override;
-  virtual bool CachedIsEmpty() override;
-  virtual bool IsSelfEmpty() override;
+  bool IsEmpty() override;
+  bool CachedIsEmpty() override;
+  bool IsSelfEmpty() override;
 
   // Given that we have a bullet, does it actually draw something, i.e.,
   // do we have either a 'list-style-type' or 'list-style-image' that is
   // not 'none'?
   bool BulletIsEmpty() const;
 
   /**
    * Return the bullet text equivalent.
@@ -267,28 +267,28 @@ public:
   /**
    * @return the bullet frame or nullptr if we don't have one.
    */
   nsBulletFrame* GetBullet() const {
     nsBulletFrame* outside = GetOutsideBullet();
     return outside ? outside : GetInsideBullet();
   }
 
-  virtual void MarkIntrinsicISizesDirty() override;
+  void MarkIntrinsicISizesDirty() override;
 private:
   void CheckIntrinsicCacheAgainstShrinkWrapState();
 public:
-  virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
-  virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
+  nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
+  nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
 
-  virtual nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
+  nsRect ComputeTightBounds(DrawTarget* aDrawTarget) const override;
 
-  virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
-                                           nscoord* aX,
-                                           nscoord* aXMost) override;
+  nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
+                                   nscoord* aX,
+                                   nscoord* aXMost) override;
 
   /**
    * Compute the final block size of this frame.
    *
    * @param aReflowInput Data structure passed from parent during reflow.
    * @param aReflowStatus A pointer to the reflow status for when we're finished
    *        doing reflow. this will get set appropriately if the block-size
    *        causes us to exceed the current available (page) block-size.
@@ -304,35 +304,35 @@ public:
    */
   void ComputeFinalBSize(const ReflowInput& aReflowInput,
                          nsReflowStatus* aStatus,
                          nscoord aContentBSize,
                          const mozilla::LogicalMargin& aBorderPadding,
                          mozilla::LogicalSize& aFinalSize,
                          nscoord aConsumed);
 
-  virtual void Reflow(nsPresContext* aPresContext,
-                      ReflowOutput& aDesiredSize,
-                      const ReflowInput& aReflowInput,
-                      nsReflowStatus& aStatus) override;
+  void Reflow(nsPresContext* aPresContext,
+              ReflowOutput& aDesiredSize,
+              const ReflowInput& aReflowInput,
+              nsReflowStatus& aStatus) override;
 
-  virtual nsresult AttributeChanged(int32_t aNameSpaceID,
-                                    nsIAtom* aAttribute,
-                                    int32_t aModType) override;
+  nsresult AttributeChanged(int32_t aNameSpaceID,
+                            nsIAtom* aAttribute,
+                            int32_t aModType) override;
 
   /**
    * Move any frames on our overflow list to the end of our principal list.
    * @return true if there were any overflow frames
    */
-  virtual bool DrainSelfOverflowList() override;
+  bool DrainSelfOverflowList() override;
 
-  virtual nsresult StealFrame(nsIFrame* aChild) override;
+  nsresult StealFrame(nsIFrame* aChild) override;
 
-  virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
-                                     bool aDeletingEmptyFrames) override;
+  void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
+                             bool aDeletingEmptyFrames) override;
 
   /**
     * This is a special method that allows a child class of nsBlockFrame to
     * return a special, customized nsStyleText object to the nsLineLayout
     * constructor. It is used when the nsBlockFrame child needs to specify its
     * custom rendering style.
     */
   virtual const nsStyleText* StyleTextForLineLayout();
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -57,33 +57,31 @@ public:
   class FlexboxAxisTracker;
   struct StrutInfo;
 
   // nsIFrame overrides
   void Init(nsIContent*       aContent,
             nsContainerFrame* aParent,
             nsIFrame*         aPrevInFlow) override;
 
-  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                const nsRect&           aDirtyRect,
-                                const nsDisplayListSet& aLists) override;
-
-  virtual void Reflow(nsPresContext*           aPresContext,
-                      ReflowOutput&     aDesiredSize,
-                      const ReflowInput& aReflowInput,
-                      nsReflowStatus&          aStatus) override;
+  void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                        const nsRect&           aDirtyRect,
+                        const nsDisplayListSet& aLists) override;
 
-  v