merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Sat, 02 Jul 2016 11:15:05 +0200
changeset 303456 39dffbba764210b25bfc1e749b4f16db77fa0d46
parent 303372 49a1d28776b4a2867bb8818dfab61c352cc02f49 (current diff)
parent 303455 b8d9d1486f5bf55ce10d082e19f511498165c2d9 (diff)
child 303457 c048aba030217a7ec09191f59de5836e9c3661a1
child 303461 81bc6dff086a1b4769398446d16f25a3f2da81a5
child 303514 73c0a2bba6db663e61a247f7e7d241ad091e4cf5
push id30388
push usercbook@mozilla.com
push dateSat, 02 Jul 2016 09:15:23 +0000
treeherdermozilla-central@39dffbba7642 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
39dffbba7642 / 50.0a1 / 20160702030219 / files
nightly linux64
39dffbba7642 / 50.0a1 / 20160702030219 / files
nightly mac
39dffbba7642 / 50.0a1 / 20160702030219 / files
nightly win32
39dffbba7642 / 50.0a1 / 20160702030219 / files
nightly win64
39dffbba7642 / 50.0a1 / 20160702030219 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/html/test/forms/test_experimental_forms_pref.html
testing/web-platform/meta/html/semantics/embedded-content/media-elements/interfaces/TextTrack/activeCues.html.ini
widget/CompositorWidgetProxy.cpp
widget/CompositorWidgetProxy.h
widget/windows/WinCompositorWidgetProxy.cpp
widget/windows/WinCompositorWidgetProxy.h
--- a/config/mozunit.py
+++ b/config/mozunit.py
@@ -180,28 +180,28 @@ class MockedOpen(object):
                 self._wrapped_isdir(p) or
                 self._orig_path_exists(p))
 
     def _wrapped_isfile(self, p):
         p = normcase(p)
         if p in self.files:
             return True
 
-        abspath = os.path.abspath(p)
+        abspath = normcase(os.path.abspath(p))
         if abspath in self.files:
             return True
 
         return self._orig_path_isfile(p)
 
     def _wrapped_isdir(self, p):
         p = normcase(p)
         p = p if p.endswith(('/', '\\')) else p + os.sep
         if any(f.startswith(p) for f in self.files):
             return True
 
-        abspath = os.path.abspath(p) + os.sep
+        abspath = normcase(os.path.abspath(p) + os.sep)
         if any(f.startswith(abspath) for f in self.files):
             return True
 
         return self._orig_path_exists(p)
 
 def main(*args, **kwargs):
     unittest.main(testRunner=MozTestRunner(), *args, **kwargs)
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -339,16 +339,20 @@ public:
   bool GetUpgradeInsecureRequests(bool aPreload) const
   {
     if (aPreload) {
       return mUpgradeInsecurePreloads;
     }
     return mUpgradeInsecureRequests;
   }
 
+  void SetReferrer(const nsACString& aReferrer) {
+    mReferrer = aReferrer;
+  }
+
   /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Return the LoadGroup for the document. May return null.
    */
--- a/dom/base/nsMimeTypeArray.cpp
+++ b/dom/base/nsMimeTypeArray.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/MimeTypeBinding.h"
 #include "nsIDOMNavigator.h"
 #include "nsPIDOMWindow.h"
 #include "nsPluginArray.h"
 #include "nsIMIMEService.h"
 #include "nsIMIMEInfo.h"
 #include "Navigator.h"
 #include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
 #include "nsPluginTags.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsMimeTypeArray)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsMimeTypeArray)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMimeTypeArray)
@@ -35,16 +36,22 @@ nsMimeTypeArray::nsMimeTypeArray(nsPIDOM
   : mWindow(aWindow)
 {
 }
 
 nsMimeTypeArray::~nsMimeTypeArray()
 {
 }
 
+static bool
+ResistFingerprinting() {
+  return !nsContentUtils::ThreadsafeIsCallerChrome() &&
+         nsContentUtils::ResistFingerprinting();
+}
+
 JSObject*
 nsMimeTypeArray::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MimeTypeArrayBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 nsMimeTypeArray::Refresh()
@@ -73,16 +80,20 @@ nsMimeTypeArray::NamedItem(const nsAStri
   return NamedGetter(aName, unused);
 }
 
 nsMimeType*
 nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound)
 {
   aFound = false;
 
+  if (ResistFingerprinting()) {
+    return nullptr;
+  }
+
   EnsurePluginMimeTypes();
 
   if (aIndex >= mMimeTypes.Length()) {
     return nullptr;
   }
 
   aFound = true;
 
@@ -103,16 +114,20 @@ FindMimeType(const nsTArray<RefPtr<nsMim
   return nullptr;
 }
 
 nsMimeType*
 nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
 {
   aFound = false;
 
+  if (ResistFingerprinting()) {
+    return nullptr;
+  }
+
   EnsurePluginMimeTypes();
 
   nsString lowerName(aName);
   ToLowerCase(lowerName);
 
   nsMimeType* mimeType = FindMimeType(mMimeTypes, lowerName);
   if (mimeType) {
     aFound = true;
@@ -120,16 +135,20 @@ nsMimeTypeArray::NamedGetter(const nsASt
   }
 
   return nullptr;
 }
 
 uint32_t
 nsMimeTypeArray::Length()
 {
+  if (ResistFingerprinting()) {
+    return 0;
+  }
+
   EnsurePluginMimeTypes();
 
   return mMimeTypes.Length();
 }
 
 void
 nsMimeTypeArray::GetSupportedNames(nsTArray<nsString>& aRetval)
 {
--- a/dom/base/nsPluginArray.cpp
+++ b/dom/base/nsPluginArray.cpp
@@ -16,16 +16,17 @@
 #include "nsPluginHost.h"
 #include "nsPluginTags.h"
 #include "nsIObserverService.h"
 #include "nsIWeakReference.h"
 #include "mozilla/Services.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIPermissionManager.h"
 #include "nsIDocument.h"
+#include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsPluginArray::nsPluginArray(nsPIDOMWindowInner* aWindow)
   : mWindow(aWindow)
 {
 }
@@ -39,16 +40,22 @@ nsPluginArray::Init()
     obsService->AddObserver(this, "plugin-info-updated", true);
   }
 }
 
 nsPluginArray::~nsPluginArray()
 {
 }
 
+static bool
+ResistFingerprinting() {
+  return !nsContentUtils::ThreadsafeIsCallerChrome() &&
+         nsContentUtils::ResistFingerprinting();
+}
+
 nsPIDOMWindowInner*
 nsPluginArray::GetParentObject() const
 {
   MOZ_ASSERT(mWindow);
   return mWindow;
 }
 
 JSObject*
@@ -165,17 +172,17 @@ nsPluginArray::Refresh(bool aReloadDocum
   }
 }
 
 nsPluginElement*
 nsPluginArray::IndexedGetter(uint32_t aIndex, bool &aFound)
 {
   aFound = false;
 
-  if (!AllowPlugins()) {
+  if (!AllowPlugins() || ResistFingerprinting()) {
     return nullptr;
   }
 
   EnsurePlugins();
 
   aFound = aIndex < mPlugins.Length();
 
   if (!aFound) {
@@ -212,17 +219,17 @@ FindPlugin(const nsTArray<RefPtr<nsPlugi
   return nullptr;
 }
 
 nsPluginElement*
 nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
 {
   aFound = false;
 
-  if (!AllowPlugins()) {
+  if (!AllowPlugins() || ResistFingerprinting()) {
     return nullptr;
   }
 
   EnsurePlugins();
 
   nsPluginElement* plugin = FindPlugin(mPlugins, aName);
   aFound = (plugin != nullptr);
   if (!aFound) {
@@ -233,17 +240,17 @@ nsPluginArray::NamedGetter(const nsAStri
     }
   }
   return plugin;
 }
 
 uint32_t
 nsPluginArray::Length()
 {
-  if (!AllowPlugins()) {
+  if (!AllowPlugins() || ResistFingerprinting()) {
     return 0;
   }
 
   EnsurePlugins();
 
   return mPlugins.Length();
 }
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -625,16 +625,17 @@ skip-if = buildapp == 'b2g'
 [test_bug1165501.html]
 [test_bug1187157.html]
 [test_bug1198095.html]
 [test_bug1238440.html]
 [test_bug1250148.html]
 [test_bug1259588.html]
 [test_bug1263696.html]
 [test_bug1274806.html]
+[test_bug1281963.html]
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_classList.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1281963.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/1281963
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Test for Bug 1281963</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+
+<script class="testbody" type="application/javascript;version=1.7">
+
+// __setPref(key, value)__.
+// Set a pref value asynchronously, returning a promise that resolves
+// when it succeeds.
+let setPref = function* (key, value) {
+  return new Promise(function(resolve, reject) {
+    SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve);
+  });
+};
+
+// Run a test to see that we don't expose the supported mimeTypes
+// or installed plugins when "privacy.resistFingerprinting" is active.
+add_task(function* () {
+  let exampleMimeType = undefined,
+      examplePlugin = undefined;
+  // Disable fingerprinting resistance.
+  yield setPref("privacy.resistFingerprinting", false);
+  // Depending on the testing platform, we may have at least
+  // one mimeType and plugin available.
+  exampleMimeType = navigator.mimeTypes[0];
+  examplePlugin = navigator.plugins[0];
+
+  // First check that we can retrieve mimeType or plugin by name and that
+  // the array length is nonzero.
+  if (exampleMimeType) {
+    isnot(navigator.mimeTypes[exampleMimeType.type], undefined, "Should reveal mime type");
+    isnot(navigator.mimeTypes.length, 0, "navigator.mimeTypes.length should be nonzero");
+  }
+  if (examplePlugin) {
+    isnot(navigator.plugins[examplePlugin.name], undefined, "Should reveal plugin");
+    isnot(navigator.plugins.length, 0, "navigator.plugins.length should be nonzero");
+  }
+
+  // Now test with fingerprinting resistance enabled
+  yield setPref("privacy.resistFingerprinting", true);
+  if (exampleMimeType) {
+    is(navigator.mimeTypes[exampleMimeType.type], undefined, "Don't reveal mime type");
+  }
+  is(navigator.mimeTypes[0], undefined, "Don't reveal mime type");
+  is(navigator.mimeTypes.length, 0, "navigator.mimeTypes.length should be 0");
+  if (examplePlugin) {
+    is(navigator.plugins[examplePlugin.name], undefined, "Don't reveal plugin");
+  }
+  is(navigator.plugins[0], undefined, "Don't reveal plugin");
+  is(navigator.plugins.length, 0, "navigator.plugins.length should be 0");
+});
+
+</script>
+
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1,19 +1,20 @@
 # -*- Mode:Python; tab-width:8; indent-tabs-mode:nil; c-basic-offset:8 -*- */
 # vim: set ts=8 sts=4 et sw=4 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/.
 
 # DOM Bindings Configuration.
 #
-# The WebIDL interfaces are defined in dom/webidl. For each such interface, there
-# is a corresponding entry in the configuration table below. The configuration
-# table maps each interface name to a |descriptor| or list of |descriptor|s.
+# The WebIDL interfaces are defined in dom/webidl. For interfaces requiring
+# special handling, there are corresponding entries in the configuration table
+# below. The configuration table maps each interface name to a |descriptor| or
+# list of |descriptor|s.
 #
 # Valid fields for all descriptors:
 #   * nativeType - The native type (concrete class or XPCOM interface) that
 #                  instances of this interface will unwrap to.  If not
 #                  specified, defaults to 'nsIDOM' followed by the interface
 #                  name for external interfaces,
 #                  'mozilla::dom::workers::InterfaceName' for worker
 #                  non-callback interfaces, and 'mozilla::dom::InterfaceName'
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -37,16 +37,17 @@
 
 #include "nsColor.h"
 #include "nsGfxCIID.h"
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsDisplayList.h"
 #include "nsFocusManager.h"
+#include "nsContentUtils.h"
 
 #include "nsTArray.h"
 
 #include "ImageEncoder.h"
 #include "ImageRegion.h"
 
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
@@ -80,16 +81,17 @@
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Tools.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/PatternHelpers.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
@@ -115,16 +117,17 @@
 #include "nsFilterInstance.h"
 #include "nsSVGLength2.h"
 #include "nsDeviceContext.h"
 #include "nsFontMetrics.h"
 #include "Units.h"
 #include "CanvasUtils.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/layers/CanvasClient.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
 #include "SkiaGLGlue.h"
 #ifdef USE_SKIA
 #include "SurfaceTypes.h"
 #include "GLBlitHelper.h"
 #endif
@@ -218,20 +221,42 @@ protected:
   friend class CanvasGeneralPattern;
 
   // Beginning of linear gradient.
   Point mBegin;
   // End of linear gradient.
   Point mEnd;
 };
 
+bool
+CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
+{
+  const ContextState& state = CurrentState();
+  if (state.globalAlpha < 1.0) {
+    return false;
+  }
+
+  if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
+    return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
+  }
+
+  // TODO: for gradient patterns we could check that all stops are opaque
+  // colors.
+
+  if (!state.gradientStyles[aStyle]) {
+    // it's a color pattern.
+    return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
+  }
+
+  return false;
+}
+
 // This class is named 'GeneralCanvasPattern' instead of just
 // 'GeneralPattern' to keep Windows PGO builds from confusing the
 // GeneralPattern class in gfxContext.cpp with this one.
-
 class CanvasGeneralPattern
 {
 public:
   typedef CanvasRenderingContext2D::Style Style;
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   Pattern& ForStyle(CanvasRenderingContext2D* aCtx,
                     Style aStyle,
@@ -716,16 +741,46 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(C
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
 
+class CanvasShutdownObserver final : public nsIObserver
+{
+public:
+  explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
+  : mCanvas(aCanvas)
+  {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+private:
+  ~CanvasShutdownObserver() {}
+
+  CanvasRenderingContext2D* mCanvas;
+};
+
+NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+CanvasShutdownObserver::Observe(nsISupports *aSubject,
+                                const char *aTopic,
+                                const char16_t *aData)
+{
+  if (mCanvas && strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    mCanvas->OnShutdown();
+    nsContentUtils::UnregisterShutdownObserver(this);
+  }
+
+  return NS_OK;
+}
+
 class CanvasDrawObserver
 {
 public:
   explicit CanvasDrawObserver(CanvasRenderingContext2D* aCanvasContext);
 
   // Only enumerate draw calls that could affect the heuristic
   enum DrawCallType {
     PutImageData,
@@ -1006,37 +1061,42 @@ CanvasRenderingContext2D::CanvasRenderin
   , mVideoTexture(0)
 #endif
   // these are the default values from the Canvas spec
   , mWidth(0), mHeight(0)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
   , mIsSkiaGL(false)
+  , mHasPendingStableStateCallback(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
   , mIsCapturedFrameInvalid(false)
   , mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
   sNumLivingContexts++;
 
+  mShutdownObserver = new CanvasShutdownObserver(this);
+  nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
+
   // The default is to use OpenGL mode
   if (gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) {
     mDrawObserver = new CanvasDrawObserver(this);
   } else {
     mRenderingMode = RenderingMode::SoftwareBackendMode;
   }
 }
 
 CanvasRenderingContext2D::~CanvasRenderingContext2D()
 {
   RemoveDrawObserver();
   RemovePostRefreshObserver();
+  RemoveShutdownObserver();
   Reset();
   // Drop references from all CanvasRenderingContext2DUserData to this context
   for (uint32_t i = 0; i < mUserDatas.Length(); ++i) {
     mUserDatas[i]->Forget();
   }
   sNumLivingContexts--;
   if (!sNumLivingContexts) {
     NS_IF_RELEASE(sErrorTarget);
@@ -1120,16 +1180,39 @@ CanvasRenderingContext2D::Reset()
   mIsEntireFrameInvalid = false;
   mPredictManyRedrawCalls = false;
   mIsCapturedFrameInvalid = false;
 
   return NS_OK;
 }
 
 void
+CanvasRenderingContext2D::OnShutdown()
+{
+  mShutdownObserver = nullptr;
+
+  RefPtr<PersistentBufferProvider> provider = mBufferProvider;
+
+  Reset();
+
+  if (provider) {
+    provider->OnShutdown();
+  }
+}
+
+void
+CanvasRenderingContext2D::RemoveShutdownObserver()
+{
+  if (mShutdownObserver) {
+    nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
+    mShutdownObserver = nullptr;
+  }
+}
+
+void
 CanvasRenderingContext2D::SetStyleFromString(const nsAString& aStr,
                                              Style aWhichStyle)
 {
   MOZ_ASSERT(!aStr.IsVoid());
 
   nscolor color;
   if (!ParseColor(aStr, &color)) {
     return;
@@ -1271,33 +1354,37 @@ bool CanvasRenderingContext2D::SwitchRen
     }
     mCurrentVideoSize.width = 0;
     mCurrentVideoSize.height = 0;
   }
 #endif
 
   RefPtr<SourceSurface> snapshot;
   Matrix transform;
+  RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
+  AutoReturnSnapshot autoReturn(nullptr);
 
   if (mTarget) {
     snapshot = mTarget->Snapshot();
     transform = mTarget->GetTransform();
   } else {
     MOZ_ASSERT(mBufferProvider);
     // When mBufferProvider is true but we have no mTarget, our current state's
     // transform is always valid. See ReturnTarget().
     transform = CurrentState().transform;
-    snapshot = mBufferProvider->GetSnapshot();
+    snapshot = mBufferProvider->BorrowSnapshot();
+    autoReturn.mBufferProvider = mBufferProvider;
+    autoReturn.mSnapshot = &snapshot;
   }
   mTarget = nullptr;
   mBufferProvider = nullptr;
   mResetLayer = true;
 
   // Recreate target using the new rendering mode
-  RenderingMode attemptedMode = EnsureTarget(aRenderingMode);
+  RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
   if (!IsTargetValid())
     return false;
 
   // We succeeded, so update mRenderingMode to reflect reality
   mRenderingMode = attemptedMode;
 
   // Restore the content from the old DrawTarget
   gfx::Rect r(0, 0, mWidth, mHeight);
@@ -1419,31 +1506,75 @@ CanvasRenderingContext2D::CheckSizeForSk
 
   double scale = gDefaultScale > 0 ? gDefaultScale : 1.0;
   int32_t threshold = ceil(scale * scale * gScreenPixels);
 
   // screen size acts as max threshold
   return threshold < 0 || (aSize.width * aSize.height) <= threshold;
 }
 
+void
+CanvasRenderingContext2D::ScheduleStableStateCallback()
+{
+  if (mHasPendingStableStateCallback) {
+    return;
+  }
+  mHasPendingStableStateCallback = true;
+
+  nsContentUtils::RunInStableState(
+    NewRunnableMethod(this, &CanvasRenderingContext2D::OnStableState)
+  );
+}
+
+void
+CanvasRenderingContext2D::OnStableState()
+{
+  ReturnTarget();
+
+  mHasPendingStableStateCallback = false;
+}
+
 CanvasRenderingContext2D::RenderingMode
-CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode)
-{
+CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
+                                       RenderingMode aRenderingMode)
+{
+  if (AlreadyShutDown()) {
+    gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
+    EnsureErrorTarget();
+    mTarget = sErrorTarget;
+    return aRenderingMode;
+  }
+
   // This would make no sense, so make sure we don't get ourselves in a mess
   MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
 
   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
 
   if (mTarget && mode == mRenderingMode) {
     return mRenderingMode;
   }
 
   if (mBufferProvider && mode == mRenderingMode) {
-    mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
+    gfx::Rect rect(0, 0, mWidth, mHeight);
+    if (aCoveredRect && CurrentState().transform.TransformBounds(*aCoveredRect).Contains(rect)) {
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect());
+    } else {
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
+    }
+
+    ScheduleStableStateCallback();
+
     if (mTarget) {
+      // Restore clip and transform.
+      mTarget->SetTransform(CurrentState().transform);
+      for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+        for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
+          mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
+        }
+      }
       return mRenderingMode;
     } else {
       mBufferProvider = nullptr;
     }
   }
 
   mIsSkiaGL = false;
 
@@ -1490,17 +1621,17 @@ CanvasRenderingContext2D::EnsureTarget(R
       }
 
       if (!mBufferProvider) {
         mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format);
       }
     }
 
     if (mBufferProvider) {
-      mTarget = mBufferProvider->GetDT(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
+      mTarget = mBufferProvider->BorrowDrawTarget(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
     } else if (!mTarget) {
       mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
       mode = RenderingMode::SoftwareBackendMode;
     }
   }
 
   if (mTarget) {
     static bool registered = false;
@@ -1618,17 +1749,22 @@ CanvasRenderingContext2D::ClearTarget()
   }
 }
 
 void
 CanvasRenderingContext2D::ReturnTarget()
 {
   if (mTarget && mBufferProvider) {
     CurrentState().transform = mTarget->GetTransform();
-    mBufferProvider->ReturnAndUseDT(mTarget.forget());
+    for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
+      for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
+        mTarget->PopClip();
+      }
+    }
+    mBufferProvider->ReturnDrawTarget(mTarget.forget());
   }
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
                                                    gfx::DrawTarget* aTarget)
 {
   RemovePostRefreshObserver();
@@ -2593,19 +2729,21 @@ void
 CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
                                     double aH)
 {
   // Do not allow zeros - it's a no-op at that point per spec.
   if (!ValidateRect(aX, aY, aW, aH, false)) {
     return;
   }
 
-  EnsureTarget();
-
-  mTarget->ClearRect(gfx::Rect(aX, aY, aW, aH));
+  gfx::Rect clearRect(aX, aY, aW, aH);
+
+  EnsureTarget(&clearRect);
+
+  mTarget->ClearRect(clearRect);
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
 
 void
 CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
                                    double aH)
 {
@@ -2656,28 +2794,32 @@ CanvasRenderingContext2D::FillRect(doubl
         aH = patternSize.height - aY;
         if (aH < 0) {
           aH = 0;
         }
       }
     }
   }
 
+  CompositionOp op = UsedOperation();
+  bool discardContent = PatternIsOpaque(Style::FILL)
+    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_DEST_OUT);
+
+  const gfx::Rect fillRect(aX, aY, aW, aH);
+  EnsureTarget(discardContent ? &fillRect : nullptr);
+
   gfx::Rect bounds;
-
-  EnsureTarget();
   if (NeedToCalculateBounds()) {
-    bounds = gfx::Rect(aX, aY, aW, aH);
-    bounds = mTarget->GetTransform().TransformBounds(bounds);
+    bounds = mTarget->GetTransform().TransformBounds(fillRect);
   }
 
   AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
     FillRect(gfx::Rect(aX, aY, aW, aH),
              CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
-             DrawOptions(state.globalAlpha, UsedOperation()));
+             DrawOptions(state.globalAlpha, op));
 
   RedrawUser(gfxRect(aX, aY, aW, aH));
 }
 
 void
 CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW,
                                      double aH)
 {
@@ -4987,19 +5129,23 @@ CanvasRenderingContext2D::DrawWindow(nsG
   if (!sw || !sh) {
     return;
   }
 
   RefPtr<gfxContext> thebes;
   RefPtr<DrawTarget> drawDT;
   // Rendering directly is faster and can be done if mTarget supports Azure
   // and does not need alpha blending.
+  // Since the pre-transaction callback calls ReturnTarget, we can't have a
+  // gfxContext wrapped around it when using a shared buffer provider because
+  // the DrawTarget's shared buffer may be unmapped in ReturnTarget.
   if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) &&
       GlobalAlpha() == 1.0f &&
-      UsedOperation() == CompositionOp::OP_OVER)
+      UsedOperation() == CompositionOp::OP_OVER &&
+      (!mBufferProvider || mBufferProvider->GetType() != LayersBackend::LAYERS_CLIENT))
   {
     thebes = gfxContext::CreateOrNull(mTarget);
     MOZ_ASSERT(thebes); // already checked the draw target above
                         // (in SupportsAzureContentForDrawTarget)
     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
                                 matrix._22, matrix._31, matrix._32));
   } else {
     drawDT =
@@ -5633,25 +5779,36 @@ void CanvasRenderingContext2D::RemoveDra
     delete mDrawObserver;
     mDrawObserver = nullptr;
   }
 }
 
 PersistentBufferProvider*
 CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
 {
+  if (AlreadyShutDown()) {
+    return nullptr;
+  }
+
   if (mBufferProvider) {
     return mBufferProvider;
   }
 
   if (!mTarget) {
     return nullptr;
   }
 
-  mBufferProvider = new PersistentBufferProviderBasic(mTarget);
+  if (aManager) {
+    mBufferProvider = aManager->CreatePersistentBufferProvider(gfx::IntSize(mWidth, mHeight),
+                                                               GetSurfaceFormat());
+  }
+
+  if (!mBufferProvider) {
+    mBufferProvider = new PersistentBufferProviderBasic(mTarget);
+  }
 
   return mBufferProvider;
 }
 
 already_AddRefed<Layer>
 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                          Layer *aOldLayer,
                                          LayerManager *aManager)
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -49,16 +49,17 @@ class CanvasPath;
 
 extern const mozilla::gfx::Float SIGMA_MAX;
 
 template<typename T> class Optional;
 
 struct CanvasBidiProcessor;
 class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
+class CanvasShutdownObserver;
 
 /**
  ** CanvasRenderingContext2D
  **/
 class CanvasRenderingContext2D final :
   public nsICanvasRenderingContextInternal,
   public nsWrapperCache
 {
@@ -540,16 +541,17 @@ public:
 
   // Given a point, return hit region ID if it exists
   nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
 
 
   // return true and fills in the bound rect if element has a hit region.
   bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
 
+  void OnShutdown();
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
                                  dom::Uint8ClampedArray* aArray,
                                  bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
@@ -630,17 +632,31 @@ protected:
    /**
    * Create the backing surfacing, if it doesn't exist. If there is an error
    * in creating the target then it will put sErrorTarget in place. If there
    * is in turn an error in creating the sErrorTarget then they would both
    * be null so IsTargetValid() would still return null.
    *
    * Returns the actual rendering mode being used by the created target.
    */
-  RenderingMode EnsureTarget(RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+  RenderingMode EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+                             RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+
+  /**
+   * This method is run at the end of the event-loop spin where
+   * ScheduleStableStateCallback was called.
+   *
+   * We use it to unlock resources that need to be locked while drawing.
+   */
+  void OnStableState();
+
+  /**
+   * Cf. OnStableState.
+   */
+  void ScheduleStableStateCallback();
 
   /**
    * Disposes an old target and prepares to lazily create a new target.
    */
   void ClearTarget();
 
   /*
    * Returns the target to the buffer provider. i.e. this will queue a frame for
@@ -657,16 +673,22 @@ protected:
 
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   /**
+   * Returns true if we know for sure that the pattern for a given style is opaque.
+   * Usefull to know if we can discard the content below in certain situations.
+   */
+  bool PatternIsOpaque(Style aStyle) const;
+
+  /**
    * Update CurrentState().filter with the filter description for
    * CurrentState().filterChain.
    */
   void UpdateFilter();
 
   nsLayoutUtils::SurfaceFromElementResult
     CachedSurfaceFromElement(Element* aElement);
 
@@ -717,16 +739,18 @@ protected:
   // recreate it (i.e. our backing surface changed)
   bool mResetLayer;
   // This is needed for drawing in drawAsyncXULElement
   bool mIPC;
   // True if the current DrawTarget is using skia-gl, used so we can avoid
   // requesting the DT from mBufferProvider to check.
   bool mIsSkiaGL;
 
+  bool mHasPendingStableStateCallback;
+
   nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
 
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
@@ -737,16 +761,20 @@ protected:
   uint32_t SkiaGLTex() const;
 
   // This observes our draw calls at the beginning of the canvas
   // lifetime and switches to software or GPU mode depending on
   // what it thinks is best
   CanvasDrawObserver* mDrawObserver;
   void RemoveDrawObserver();
 
+  RefPtr<CanvasShutdownObserver> mShutdownObserver;
+  void RemoveShutdownObserver();
+  bool AlreadyShutDown() const { return !mShutdownObserver; }
+
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
     * mIsEntireFrameInvalid since we expect it will be followed by
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -92,28 +92,28 @@ static const gl::GLFeature kRequiredFeat
     gl::GLFeature::texture_storage,
     gl::GLFeature::transform_feedback2,
     gl::GLFeature::uniform_buffer_object,
     gl::GLFeature::uniform_matrix_nonsquare,
     gl::GLFeature::vertex_array_object
 };
 
 bool
-WebGLContext::InitWebGL2(nsACString* const out_failReason, nsACString* const out_failureId)
+WebGLContext::InitWebGL2(FailureReason* const out_failReason)
 {
     MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!");
 
     // check OpenGL features
     if (!gl->IsSupported(gl::GLFeature::occlusion_query) &&
         !gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
     {
         // On desktop, we fake occlusion_query_boolean with occlusion_query if
         // necessary. (See WebGL2ContextQueries.cpp)
-        *out_failureId = "FEATURE_FAILURE_WEBGL2_OCCL";
-        out_failReason->AssignASCII("WebGL 2 requires occlusion query support.");
+        *out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL",
+                                        "WebGL 2 requires occlusion query support.");
         return false;
     }
 
     std::vector<gl::GLFeature> missingList;
 
     for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
         if (!gl->IsSupported(kRequiredFeatures[i])) {
             missingList.push_back(kRequiredFeatures[i]);
@@ -131,21 +131,20 @@ WebGLContext::InitWebGL2(nsACString* con
 
     if (missingList.size()) {
         nsAutoCString exts;
         for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
             exts.AppendLiteral("\n  ");
             exts.Append(gl::GLContext::GetFeatureName(*itr));
         }
 
-        *out_failureId = "FEATURE_FAILURE_WEBGL2_FEATURE";
         const nsPrintfCString reason("WebGL 2 requires support for the following"
                                      " features: %s",
                                      exts.BeginReading());
-        out_failReason->Assign(reason);
+        *out_failReason = FailureReason("FEATURE_FAILURE_WEBGL2_OCCL", reason);
         return false;
     }
 
     // we initialise WebGL 2 related stuff.
     gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
                      &mGLMaxTransformFeedbackSeparateAttribs);
     gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
                      &mGLMaxUniformBufferBindings);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -72,16 +72,20 @@
 #include "WebGLTransformFeedback.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 #ifdef MOZ_WIDGET_COCOA
 #include "nsCocoaFeatures.h"
 #endif
 
+#ifdef XP_WIN
+#include "WGLLibrary.h"
+#endif
+
 // Generated
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
@@ -429,22 +433,25 @@ WebGLContext::GetHeight() const
  *
  * That is, try to create headless contexts based on the platform API.
  * Next, create dummy-sized backbuffers for the contexts with the right
  * caps. Finally, resize the backbuffer to an acceptable size given the
  * requested size.
  */
 
 static bool
-IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature, nsACString* const out_failureId)
+IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature,
+                     nsCString* const out_blacklistId)
 {
     int32_t status;
     if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
-                                                           *out_failureId, &status)))
+                                                           *out_blacklistId, &status)))
+    {
         return false;
+    }
 
     return status != nsIGfxInfo::FEATURE_STATUS_OK;
 }
 
 static bool
 HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
 {
     int32_t status;
@@ -557,186 +564,216 @@ BaseCaps(const WebGLContextOptions& opti
             break;
 
         baseCaps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
     } while (false);
 #endif
 
     // Done with baseCaps construction.
 
-    bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
-    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-    nsCString discardFailureId;
-    if (!forceAllowAA &&
-        IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA, &discardFailureId))
-    {
-        webgl->GenerateWarning("Disallowing antialiased backbuffers due"
-                               " to blacklisting.");
-        baseCaps.antialias = false;
+    if (!gfxPrefs::WebGLForceMSAA()) {
+        const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+
+        nsCString blocklistId;
+        if (IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA, &blocklistId)) {
+            webgl->GenerateWarning("Disallowing antialiased backbuffers due"
+                                   " to blacklisting.");
+            baseCaps.antialias = false;
+        }
     }
 
     return baseCaps;
 }
 
 ////////////////////////////////////////
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                WebGLContext* webgl, nsACString* const out_failReason,
-                nsACString* const out_failureId)
+                WebGLContext* webgl,
+                std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
     const gfx::IntSize dummySize(16, 16);
+    nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags, out_failureId);
+                                                                     flags, &failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        if (out_failReason->Length()) {
-            out_failReason->AppendLiteral("\n");
-        }
-        out_failReason->AppendLiteral("Error during EGL OpenGL init.");
-        if (out_failureId->IsEmpty()) {
-            *out_failureId = "FEATURE_FAILURE_WEBGL_EGL_INIT";
-        }
+        out_failReasons->push_back(WebGLContext::FailureReason(
+            failureId,
+            "Error during EGL OpenGL init."
+        ));
         return nullptr;
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                  WebGLContext* webgl, nsACString* const out_failReason,
-                  nsACString* const out_failureId)
+                  WebGLContext* webgl,
+                  std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
     const gfx::IntSize dummySize(16, 16);
+    nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
-                                                                     flags, out_failureId);
+                                                                     flags, &failureId);
     if (gl && !gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        if (out_failReason->Length()) {
-            out_failReason->AppendLiteral("\n");
-        }
-        out_failReason->AppendLiteral("Error during ANGLE OpenGL init.");
-        if (out_failureId->IsEmpty()) {
-            *out_failureId = "FEATURE_FAILURE_WEBGL_ANGLE_INIT";
-        }
+        out_failReasons->push_back(WebGLContext::FailureReason(
+            failureId,
+            "Error during ANGLE OpenGL init."
+        ));
         return nullptr;
     }
 
     return gl.forget();
 }
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
-                    WebGLContext* webgl, nsACString* const out_failReason,
-                    nsACString* const out_failureId)
+                    WebGLContext* webgl,
+                    std::vector<WebGLContext::FailureReason>* const out_failReasons)
 {
-    nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-
-    if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
-        IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL, out_failureId))
-    {
-        if (out_failReason->Length()) {
-            out_failReason->AppendASCII("\n");
-        }
-        out_failReason->AppendASCII("Refused to create native OpenGL context because of"
-                                    " blacklisting.");
-        return nullptr;
-    }
-
     const gfx::IntSize dummySize(16, 16);
+    nsCString failureId;
     RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps,
-                                                                  flags, out_failureId);
-
+                                                                  flags, &failureId);
     if (gl && gl->IsANGLE()) {
         gl = nullptr;
     }
 
     if (!gl) {
-        if (out_failReason->Length()) {
-            out_failReason->AppendASCII("\n");
-        }
-        out_failReason->AppendASCII("Error during native OpenGL init.");
-        if (out_failureId->IsEmpty()) {
-            *out_failureId = "FEATURE_FAILURE_WEBGL_DEFAULT_INIT";
-        }
+        out_failReasons->push_back(WebGLContext::FailureReason(
+            failureId,
+            "Error during native OpenGL init."
+        ));
         return nullptr;
     }
 
     return gl.forget();
 }
 
 ////////////////////////////////////////
 
 bool
 WebGLContext::CreateAndInitGLWith(FnCreateGL_T fnCreateGL,
                                   const gl::SurfaceCaps& baseCaps,
                                   gl::CreateContextFlags flags,
-                                  nsACString* const out_failReason,
-                                  nsACString* const out_failureId)
+                                  std::vector<FailureReason>* const out_failReasons)
 {
     std::queue<gl::SurfaceCaps> fallbackCaps;
     PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
 
     MOZ_RELEASE_ASSERT(!gl, "GFX: Already have a context.");
     gl = nullptr;
     while (!fallbackCaps.empty()) {
-        gl::SurfaceCaps& caps = fallbackCaps.front();
-
-        gl = fnCreateGL(caps, flags, this, out_failReason, out_failureId);
+        const gl::SurfaceCaps& caps = fallbackCaps.front();
+        gl = fnCreateGL(caps, flags, this, out_failReasons);
         if (gl)
             break;
 
         fallbackCaps.pop();
     }
     if (!gl)
         return false;
 
-    if (!InitAndValidateGL(out_failReason, out_failureId)) {
+    FailureReason reason;
+    if (!InitAndValidateGL(&reason)) {
         // The fail reason here should be specific enough for now.
         gl = nullptr;
+        out_failReasons->push_back(reason);
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLContext::CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason, nsACString* const out_failureId)
+WebGLContext::CreateAndInitGL(bool forceEnabled,
+                              std::vector<FailureReason>* const out_failReasons)
 {
-    const bool useEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
-
-    bool useANGLE = false;
-#ifdef XP_WIN
-    const bool disableANGLE = (gfxPrefs::WebGLDisableANGLE() ||
-                               PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"));
-    useANGLE = !disableANGLE;
-#endif
-
+    const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
     gl::CreateContextFlags flags = gl::CreateContextFlags::NO_VALIDATION;
-
-    if (forceEnabled) flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
-    if (!IsWebGL2())  flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
-    if (IsWebGL2())   flags |= gl::CreateContextFlags::PREFER_ES3;
-
-    const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
-
-    if (useEGL)
-        return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags, out_failReason, out_failureId);
-
-    if (useANGLE)
-        return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReason, out_failureId);
-
-    return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReason, out_failureId);
+    bool tryNativeGL = true;
+    bool tryANGLE = false;
+
+    if (forceEnabled) {
+        flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
+    }
+
+    if (IsWebGL2()) {
+        flags |= gl::CreateContextFlags::PREFER_ES3;
+    } else {
+        flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
+    }
+
+    //////
+
+    const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
+
+#ifdef XP_WIN
+    if (!IsWebGL2()) {
+        // Use only ANGLE on Windows for WebGL 1.
+        tryNativeGL = false;
+        tryANGLE = true;
+    }
+
+    if (gfxPrefs::WebGLDisableWGL()) {
+        tryNativeGL = false;
+    }
+
+    if (gfxPrefs::WebGLDisableANGLE() || PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") || useEGL) {
+        tryNativeGL = true;
+        tryANGLE = false;
+    }
+#endif
+
+    if (tryNativeGL && !forceEnabled) {
+        const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+        const auto feature = nsIGfxInfo::FEATURE_WEBGL_OPENGL;
+
+        FailureReason reason;
+        if (IsFeatureInBlacklist(gfxInfo, feature, &reason.key)) {
+            reason.info = "Refused to create native OpenGL context because of blacklist"
+                          " entry: ";
+            reason.info.Append(reason.key);
+
+            out_failReasons->push_back(reason);
+
+            GenerateWarning(reason.info.BeginReading());
+            tryNativeGL = false;
+        }
+    }
+
+    //////
+
+    if (tryNativeGL) {
+        if (useEGL)
+            return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags, out_failReasons);
+
+        if (CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReasons))
+            return true;
+    }
+
+    //////
+
+    if (tryANGLE)
+        return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReasons);
+
+    //////
+
+    out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
+                                             "Exhausted GL driver options."));
+    return false;
 }
 
 // Fallback for resizes:
 bool
 WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
                                uint32_t requestedHeight)
 {
     uint32_t width = requestedWidth;
@@ -924,37 +961,54 @@ WebGLContext::SetDimensions(int32_t sign
         }
     }
 
     // Alright, now let's start trying.
     bool forceEnabled = gfxPrefs::WebGLForceEnabled();
     ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
 
     MOZ_ASSERT(!gl);
-    nsCString failReason;
-    nsCString failureId;
-    if (!CreateAndInitGL(forceEnabled, &failReason, &failureId)) {
-        Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
-        const nsPrintfCString text("WebGL creation failed: %s",
-                                   failReason.BeginReading());
+    std::vector<FailureReason> failReasons;
+    if (!CreateAndInitGL(forceEnabled, &failReasons)) {
+        nsCString text("WebGL creation failed: ");
+        for (const auto& cur : failReasons) {
+            Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
+
+            text.AppendASCII("\n* ");
+            text.Append(cur.info);
+        }
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
     MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
 
     if (mOptions.failIfMajorPerformanceCaveat) {
         if (gl->IsWARP()) {
+            gl = nullptr;
+
             Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
                                   NS_LITERAL_CSTRING("FEATURE_FAILURE_PERF_WARP"));
             const nsLiteralCString text("failIfMajorPerformanceCaveat: Driver is not"
                                         " hardware-accelerated.");
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
+
+#ifdef XP_WIN
+        if (gl->GetContextType() == gl::GLContextType::WGL &&
+            !gl::sWGLLib.HasDXInterop2())
+        {
+            gl = nullptr;
+
+            const nsLiteralCString text("Caveat: WGL without DXGLInterop2.");
+            ThrowEvent_WebGLContextCreationError(text);
+            return NS_ERROR_FAILURE;
+        }
+#endif
     }
 
     if (!ResizeBackbuffer(width, height)) {
         Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
                               NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_RESIZE"));
         const nsLiteralCString text("Initializing WebGL backbuffer failed.");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1193,37 +1193,51 @@ protected:
 
     nsTArray<GLenum> mCompressedTextureFormats;
 
     // -------------------------------------------------------------------------
     // WebGL 2 specifics (implemented in WebGL2Context.cpp)
 public:
     virtual bool IsWebGL2() const = 0;
 
+    struct FailureReason {
+        nsCString key; // For reporting.
+        nsCString info;
+
+        FailureReason() { }
+
+        template<typename A, typename B>
+        FailureReason(const A& _key, const B& _info)
+            : key(nsCString(_key))
+            , info(nsCString(_info))
+        { }
+    };
 protected:
-    bool InitWebGL2(nsACString* const out_failReason, nsACString* const out_failureId);
+    bool InitWebGL2(FailureReason* const out_failReason);
 
-    bool CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason, nsACString* const out_failureId);
+    bool CreateAndInitGL(bool forceEnabled,
+                         std::vector<FailureReason>* const out_failReasons);
+
     bool ResizeBackbuffer(uint32_t width, uint32_t height);
 
     typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
                                                          gl::CreateContextFlags flags,
                                                          WebGLContext* webgl,
-                                                         nsACString* const out_failReason,
-                                                         nsACString* const out_failureId);
+                                                         std::vector<FailureReason>* const out_failReasons);
 
     bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
                              gl::CreateContextFlags flags,
-                             nsACString* const out_failReason,
-                             nsACString* const out_failureId);
+                             std::vector<FailureReason>* const out_failReasons);
+
     void ThrowEvent_WebGLContextCreationError(const nsACString& text);
 
     // -------------------------------------------------------------------------
     // Validation functions (implemented in WebGLContextValidate.cpp)
-    bool InitAndValidateGL(nsACString* const out_failReason, nsACString* const out_failureId);
+    bool InitAndValidateGL(FailureReason* const out_failReason);
+
     bool ValidateBlendEquationEnum(GLenum cap, const char* info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
                                              const char* info);
     bool ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info);
     bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
     bool ValidateTextureTargetEnum(GLenum target, const char* info);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -643,37 +643,36 @@ FloorPOT(int32_t x)
         if (x < pot*2)
             break;
         pot *= 2;
     }
     return pot;
 }
 
 bool
-WebGLContext::InitAndValidateGL(nsACString* const out_failReason, nsACString* const out_failureId)
+WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
 {
     MOZ_RELEASE_ASSERT(gl, "GFX: GL not initialized");
 
     // Unconditionally create a new format usage authority. This is
     // important when restoring contexts and extensions need to add
     // formats back into the authority.
     mFormatUsage = CreateFormatUsage(gl);
     if (!mFormatUsage) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_FORMAT";
-        out_failReason->AssignLiteral("Failed to create mFormatUsage.");
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_FORMAT",
+                            "Failed to create mFormatUsage." };
         return false;
     }
 
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_GLERR_1";
         const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
                                      " initialization, before WebGL initialization!",
                                      error);
-        out_failReason->Assign(reason);
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_1", reason };
         return false;
     }
 
     mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
     mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
@@ -752,36 +751,34 @@ WebGLContext::InitAndValidateGL(nsACStri
         gl->fEnableVertexAttribArray(0);
 
     if (MinCapabilityMode())
         mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
     else
         gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
 
     if (mGLMaxVertexAttribs < 8) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_V_ATRB";
         const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
                                      mGLMaxVertexAttribs);
-        out_failReason->Assign(reason);
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_V_ATRB", reason };
         return false;
     }
 
     // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
     // even though the hardware supports much more.  The
     // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
     if (MinCapabilityMode())
         mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
     else
         gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);
 
     if (mGLMaxTextureUnits < 8) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_T_UNIT";
         const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
                                      mGLMaxTextureUnits);
-        out_failReason->Assign(reason);
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_T_UNIT", reason };
         return false;
     }
 
     mBound2DTextures.SetLength(mGLMaxTextureUnits);
     mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
     mBound3DTextures.SetLength(mGLMaxTextureUnits);
     mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
     mBoundSamplers.SetLength(mGLMaxTextureUnits);
@@ -927,41 +924,40 @@ WebGLContext::InitAndValidateGL(nsACStri
         gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
     }
 
     // Check the shader validator pref
     mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
 
     // initialize shader translator
     if (!ShInitialize()) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_GLSL";
-        out_failReason->AssignLiteral("GLSL translator initialization failed!");
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLSL",
+                            "GLSL translator initialization failed!" };
         return false;
     }
 
     // Mesa can only be detected with the GL_VERSION string, of the form
     // "2.1 Mesa 7.11.0"
     const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
     mIsMesa = strstr(versionStr, "Mesa");
 
     // Notice that the point of calling fGetError here is not only to check for
     // errors, but also to reset the error flags so that a subsequent WebGL
     // getError call will give the correct result.
     error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
-        *out_failureId = "FEATURE_FAILURE_WEBGL_GLERR_2";
         const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
                                      " initialization!",
                                      error);
-        out_failReason->Assign(reason);
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_GLERR_2", reason };
         return false;
     }
 
     if (IsWebGL2() &&
-        !InitWebGL2(out_failReason, out_failureId))
+        !InitWebGL2(out_failReason))
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
     // Default value for all disabled vertex attributes is [0, 0, 0, 1]
     mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
     for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -4,26 +4,29 @@
  * 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 "pk11pub.h"
 #include "cryptohi.h"
 #include "secerr.h"
 #include "ScopedNSSTypes.h"
 #include "nsNSSComponent.h"
+#include "nsProxyRelease.h"
 
 #include "jsapi.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/CryptoBuffer.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mozilla/dom/KeyAlgorithmProxy.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/WebCryptoTask.h"
 #include "mozilla/dom/WebCryptoThreadPool.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
 // Template taken from security/nss/lib/util/templates.c
 // This (or SGN_EncodeDigestInfo) would ideally be exported
 // by NSS and until that happens we have to keep our own copy.
 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
     { SEC_ASN1_SEQUENCE,
       0, NULL, sizeof(SGNDigestInfo) },
     { SEC_ASN1_INLINE,
@@ -32,16 +35,21 @@ const SEC_ASN1Template SGN_DigestInfoTem
     { SEC_ASN1_OCTET_STRING,
       offsetof(SGNDigestInfo,digest) },
     { 0, }
 };
 
 namespace mozilla {
 namespace dom {
 
+using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
+using mozilla::dom::workers::Status;
+using mozilla::dom::workers::WorkerHolder;
+using mozilla::dom::workers::WorkerPrivate;
+
 // Pre-defined identifiers for telemetry histograms
 
 enum TelemetryMethod {
   TM_ENCRYPT      = 0,
   TM_DECRYPT      = 1,
   TM_SIGN         = 2,
   TM_VERIFY       = 3,
   TM_DIGEST       = 4,
@@ -128,16 +136,56 @@ public:
   {
     JS_ClearPendingException(mCx);
   }
 
 private:
   JSContext* mCx;
 };
 
+class WebCryptoTask::InternalWorkerHolder final : public WorkerHolder
+{
+  InternalWorkerHolder()
+  { }
+
+  ~InternalWorkerHolder()
+  {
+    NS_ASSERT_OWNINGTHREAD(InternalWorkerHolder);
+    // Nothing to do here since the parent destructor releases the
+    // worker automatically.
+  }
+
+public:
+  static already_AddRefed<InternalWorkerHolder>
+  Create()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    RefPtr<InternalWorkerHolder> ref = new InternalWorkerHolder();
+    if (NS_WARN_IF(!ref->HoldWorker(workerPrivate))) {
+      return nullptr;
+    }
+    return ref.forget();
+  }
+
+  virtual bool
+  Notify(Status aStatus) override
+  {
+    NS_ASSERT_OWNINGTHREAD(InternalWorkerHolder);
+    // Do nothing here.  Since WebCryptoTask dispatches back to
+    // the worker thread using nsThread::Dispatch() instead of
+    // WorkerRunnable it will always be able to execute its
+    // runnables.
+    return true;
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(WebCryptoTask::InternalWorkerHolder)
+};
+
 template<class OOS>
 static nsresult
 GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm, nsString& aName)
 {
   ClearException ce(aCx);
 
   if (aAlgorithm.IsString()) {
     // If string, then treat as algorithm name
@@ -330,18 +378,34 @@ WebCryptoTask::DispatchWithPromise(Promi
 
   // Skip NSS if we're already done, or launch a CryptoTask
   if (mEarlyComplete) {
     CallCallback(mEarlyRv);
     Skip();
     return;
   }
 
-  // Store calling thread and dispatch to thread pool.
+  // Store calling thread
   mOriginalThread = NS_GetCurrentThread();
+
+  // If we are running on a worker thread we must hold the worker
+  // alive while we work on the thread pool.  Otherwise the worker
+  // private may get torn down before we dispatch back to complete
+  // the transaction.
+  if (!NS_IsMainThread()) {
+    mWorkerHolder = InternalWorkerHolder::Create();
+    // If we can't register a holder then the worker is already
+    // shutting down.  Don't start new work.
+    if (!mWorkerHolder) {
+      mEarlyRv = NS_BINDING_ABORTED;
+    }
+  }
+  MAYBE_EARLY_FAIL(mEarlyRv);
+
+  // dispatch to thread pool
   mEarlyRv = WebCryptoThreadPool::Dispatch(this);
   MAYBE_EARLY_FAIL(mEarlyRv)
 }
 
 NS_IMETHODIMP
 WebCryptoTask::Run()
 {
   // Run heavy crypto operations on the thread pool, off the original thread.
@@ -363,30 +427,43 @@ WebCryptoTask::Run()
 
   // Release NSS resources now, before calling CallCallback, so that
   // WebCryptoTasks have consistent behavior regardless of whether NSS is shut
   // down between CalculateResult being called and CallCallback being called.
   virtualDestroyNSSReference();
 
   CallCallback(mRv);
 
+  // Stop holding the worker thread alive now that the async work has
+  // been completed.
+  mWorkerHolder = nullptr;
+
+  return NS_OK;
+}
+
+nsresult
+WebCryptoTask::Cancel()
+{
+  MOZ_ASSERT(IsOnOriginalThread());
+  FailWithError(NS_BINDING_ABORTED);
   return NS_OK;
 }
 
 void
 WebCryptoTask::FailWithError(nsresult aRv)
 {
   MOZ_ASSERT(IsOnOriginalThread());
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
 
   // Blindly convert nsresult to DOMException
   // Individual tasks must ensure they pass the right values
   mResultPromise->MaybeReject(aRv);
   // Manually release mResultPromise while we're on the main thread
   mResultPromise = nullptr;
+  mWorkerHolder = nullptr;
   Cleanup();
 }
 
 nsresult
 WebCryptoTask::CalculateResult()
 {
   MOZ_ASSERT(!IsOnOriginalThread());
 
@@ -3626,10 +3703,33 @@ WebCryptoTask::CreateUnwrapKeyTask(nsIGl
     return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey,
                                       aUnwrappingKey, aUnwrapAlgorithm,
                                       importTask);
   }
 
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
+WebCryptoTask::WebCryptoTask()
+  : mEarlyRv(NS_OK)
+  , mEarlyComplete(false)
+  , mOriginalThread(nullptr)
+  , mReleasedNSSResources(false)
+  , mRv(NS_ERROR_NOT_INITIALIZED)
+{
+}
+
+WebCryptoTask::~WebCryptoTask()
+{
+  MOZ_ASSERT(mReleasedNSSResources);
+
+  nsNSSShutDownPreventionLock lock;
+  if (!isAlreadyShutDown()) {
+    shutdown(calledFromObject);
+  }
+
+  if (mWorkerHolder) {
+    NS_ProxyRelease(mOriginalThread, mWorkerHolder.forget());
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/crypto/WebCryptoTask.h
+++ b/dom/crypto/WebCryptoTask.h
@@ -164,33 +164,18 @@ public:
                           bool aExtractable,
                           const Sequence<nsString>& aKeyUsages);
 
 protected:
   RefPtr<Promise> mResultPromise;
   nsresult mEarlyRv;
   bool mEarlyComplete;
 
-  WebCryptoTask()
-    : mEarlyRv(NS_OK)
-    , mEarlyComplete(false)
-    , mOriginalThread(nullptr)
-    , mReleasedNSSResources(false)
-    , mRv(NS_ERROR_NOT_INITIALIZED)
-  {}
-
-  virtual ~WebCryptoTask()
-  {
-    MOZ_ASSERT(mReleasedNSSResources);
-
-    nsNSSShutDownPreventionLock lock;
-    if (!isAlreadyShutDown()) {
-      shutdown(calledFromObject);
-    }
-  }
+  WebCryptoTask();
+  virtual ~WebCryptoTask();
 
   bool IsOnOriginalThread() {
     return !mOriginalThread || NS_GetCurrentThread() == mOriginalThread;
   }
 
   // For things that need to happen on the main thread
   // either before or after CalculateResult
   virtual nsresult BeforeCrypto() { return NS_OK; }
@@ -206,29 +191,33 @@ protected:
   virtual void ReleaseNSSResources() {}
 
   virtual nsresult CalculateResult() final;
 
   virtual void CallCallback(nsresult rv) final;
 
 private:
   NS_IMETHOD Run() override final;
+  nsresult Cancel() override final;
 
   virtual void
   virtualDestroyNSSReference() override final
   {
     MOZ_ASSERT(IsOnOriginalThread());
 
     if (!mReleasedNSSResources) {
       mReleasedNSSResources = true;
       ReleaseNSSResources();
     }
   }
 
+  class InternalWorkerHolder;
+
   nsCOMPtr<nsIThread> mOriginalThread;
+  RefPtr<InternalWorkerHolder> mWorkerHolder;
   bool mReleasedNSSResources;
   nsresult mRv;
 };
 
 // XXX This class is declared here (unlike others) to enable reuse by WebRTC.
 class GenerateAsymmetricKeyTask : public WebCryptoTask
 {
 public:
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -21,14 +21,15 @@ UNIFIED_SOURCES += [
     'WebCryptoThreadPool.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
+    '/dom/workers',
     '/security/manager/ssl',
     '/security/pkix/include',
     '/xpcom/build',
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
--- a/dom/crypto/test/test_WebCrypto_Workers.html
+++ b/dom/crypto/test/test_WebCrypto_Workers.html
@@ -74,16 +74,59 @@ TestArray.addTest(
       crypto.subtle.encrypt(alg, key, data).then(ciphertext => {
         // Verify and decrypt.
         crypto.subtle.decrypt(alg, key, ciphertext)
           .then(memcmp_complete(that, data), error(that));
       });
     });
   }
 );
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Web crypto in terminating Worker",
+  function () {
+    var worker = new Worker(`data:text/plain,
+      function infiniteEncrypt(key, data, nonce) {
+        var alg = { name: "AES-GCM", iv: nonce };
+        return crypto.subtle.encrypt(alg, key, data).then(_ => {
+          infiniteEncrypt(key, data, nonce);
+        });
+      }
+      onmessage = ({data: {key, data, nonce}}) => {
+        infiniteEncrypt(key, data, nonce);
+        postMessage("started");
+      };
+    `);
+
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var nonce = crypto.getRandomValues(new Uint8Array(16));
+    var alg = { name: "AES-GCM", length: 128 };
+    var that = this;
+
+    // Generate a new AES key.
+    crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then(key => {
+      worker.addEventListener("message", ({data: msg}) => {
+          if (msg === "started") {
+            // Terminate the worker while its busy doing crypto work
+            worker.terminate();
+            worker = null;
+
+            // Just end the test immediate since we can't receive any
+            // more messages from the worker after calling terminate().
+            // If we haven't crashed, then the test is a success.
+            that.complete(true);
+          }
+      });
+
+      // Send it to the worker.
+      worker.postMessage({key, data, nonce});
+    });
+  }
+);
 /*]]>*/</script>
 </head>
 
 <body>
 
 <div id="content">
 	<div id="head">
 		<b>Web</b>Crypto<br>
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -348,18 +348,17 @@ HTMLCanvasElementObserver::HandleEvent(n
 NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
 
 // ---------------------------------------------------------------------------
 
 HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mResetLayer(true) ,
     mWriteOnly(false)
-{
-}
+{}
 
 HTMLCanvasElement::~HTMLCanvasElement()
 {
   if (mContextObserver) {
     mContextObserver->Destroy();
     mContextObserver = nullptr;
   }
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2594,16 +2594,17 @@ HTMLMediaElement::PlayInternal(bool aCal
       break;
     case nsIDOMHTMLMediaElement::HAVE_METADATA:
     case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
       FireTimeUpdate(false);
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       break;
     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
+      FireTimeUpdate(false);
       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
   mPaused = false;
   mAutoplaying = false;
   SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -13,16 +13,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIObserver.h"
 #include "mozilla/CORSMode.h"
 #include "DecoderTraits.h"
 #include "nsIAudioChannelAgent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrackManager.h"
+#include "mozilla/WeakPtr.h"
 #include "MediaDecoder.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeys.h"
 #endif
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
 #include "PrincipalChangeObserver.h"
 
@@ -76,29 +77,32 @@ class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIDOMHTMLMediaElement,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback,
-                         public PrincipalChangeObserver<DOMMediaStream>
+                         public PrincipalChangeObserver<DOMMediaStream>,
+                         public SupportsWeakPtr<HTMLMediaElement>
 {
   friend AutoNotifyAudioChannelAgent;
 
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
   typedef mozilla::MediaStream MediaStream;
   typedef mozilla::MediaResource MediaResource;
   typedef mozilla::MediaDecoderOwner MediaDecoderOwner;
   typedef mozilla::MetadataTags MetadataTags;
 
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(HTMLMediaElement)
+
   CORSMode GetCORSMode() {
     return mCORSMode;
   }
 
   explicit HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   /**
    * This is used when the browser is constructing a video element to play
@@ -704,16 +708,24 @@ public:
     }
   }
   void NotifyCueUpdated(TextTrackCue *aCue) {
     if (mTextTrackManager) {
       mTextTrackManager->NotifyCueUpdated(aCue);
     }
   }
 
+  bool GetHasUserInteraction()
+  {
+    return mHasUserInteraction;
+  }
+
+  // A method to check whether we are currently playing.
+  bool IsCurrentlyPlaying() const;
+
   /**
    * A public wrapper for FinishDecoderSetup()
    */
   nsresult FinishDecoderSetup(MediaDecoder* aDecoder, MediaResource* aStream) {
     return FinishDecoderSetup(aDecoder, aStream, nullptr);
   }
 
   // Returns true if the media element is being destroyed. Used in
@@ -1129,19 +1141,16 @@ protected:
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
   // next block of audio samples) preceeding seek target.
   already_AddRefed<Promise> Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
 
   // A method to check if we are playing through the AudioChannel.
   bool IsPlayingThroughTheAudioChannel() const;
 
-  // A method to check whether we are currently playing.
-  bool IsCurrentlyPlaying() const;
-
   // Update the audio channel playing state
   void UpdateAudioChannelPlayingState();
 
   // Adds to the element's list of pending text tracks each text track
   // in the element's list of text tracks whose text track mode is not disabled
   // and whose text track readiness state is loading.
   void PopulatePendingTextTrackList();
 
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -519,17 +519,18 @@ private:
 
 void
 TextTrackManager::DispatchTimeMarchesOn()
 {
   // Run the algorithm if no previous instance is still running, otherwise
   // enqueue the current playback position and whether only that changed
   // through its usual monotonic increase during normal playback; current
   // executing call upon completion will check queue for further 'work'.
-  if (!mTimeMarchesOnDispatched && !mShutdown) {
+  if (!mTimeMarchesOnDispatched && !mShutdown &&
+      (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
     NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn));
     mTimeMarchesOnDispatched = true;
   }
 }
 
 // https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on
 void
 TextTrackManager::TimeMarchesOn()
@@ -565,16 +566,17 @@ TextTrackManager::TimeMarchesOn()
     new TextTrackCueList(window);
   RefPtr<TextTrackCueList> otherCues =
     new TextTrackCueList(window);
   bool dummy;
   for (uint32_t index = 0; index < mTextTracks->Length(); ++index) {
     TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
     if (ttrack && dummy) {
       // TODO: call GetCueListByTimeInterval on mNewCues?
+      ttrack->UpdateActiveCueList();
       TextTrackCueList* activeCueList = ttrack->GetActiveCues();
       if (activeCueList) {
         for (uint32_t i = 0; i < activeCueList->Length(); ++i) {
           currentCues->AddCue(*((*activeCueList)[i]));
         }
       }
     }
   }
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -11,16 +11,18 @@
 #include "mozilla/StateMirroring.h"
 
 #include "MediaEventSource.h"
 #include "MediaInfo.h"
 #include "nsISupports.h"
 #include "nsDataHashtable.h"
 #include "nsThreadUtils.h"
 
+class GMPCrashHelper;
+
 namespace mozilla
 {
 
 namespace layers
 {
   class ImageContainer;
 } // namespace layers
 class MediaResource;
@@ -84,16 +86,18 @@ public:
 
   // Returns the owner of this media decoder. The owner should only be used
   // on the main thread.
   virtual MediaDecoderOwner* GetOwner() = 0;
 
   // Set by Reader if the current audio track can be offloaded
   virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
 
+  virtual already_AddRefed<GMPCrashHelper> GetCrashHelper() { return nullptr; }
+
   // Stack based class to assist in notifying the frame statistics of
   // parsed and decoded frames. Use inside video demux & decode functions
   // to ensure all parsed and decoded frames are reported on all return paths.
   class AutoNotifyDecoded {
   public:
     explicit AutoNotifyDecoded(AbstractMediaDecoder* aDecoder)
       : mParsed(0), mDecoded(0), mDropped(0), mDecoder(aDecoder) {}
     ~AutoNotifyDecoded() {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Telemetry.h"
+#include "GMPService.h"
 
 #ifdef MOZ_ANDROID_OMX
 #include "AndroidBridge.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::media;
@@ -1103,16 +1104,44 @@ MediaDecoder::IsEndedOrShutdown() const
 
 bool
 MediaDecoder::OwnerHasError() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mShuttingDown || mOwner->HasError();
 }
 
+class MediaElementGMPCrashHelper : public GMPCrashHelper
+{
+public:
+  explicit MediaElementGMPCrashHelper(HTMLMediaElement* aElement)
+    : mElement(aElement)
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+  }
+  already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() override
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+    if (!mElement) {
+      return nullptr;
+    }
+    return do_AddRef(mElement->OwnerDoc()->GetInnerWindow());
+  }
+private:
+  WeakPtr<HTMLMediaElement> mElement;
+};
+
+already_AddRefed<GMPCrashHelper>
+MediaDecoder::GetCrashHelper()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mOwner->GetMediaElement() ?
+    MakeAndAddRef<MediaElementGMPCrashHelper>(mOwner->GetMediaElement()) : nullptr;
+}
+
 bool
 MediaDecoder::IsEnded() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mPlayState == PLAY_STATE_ENDED ||
          (mWasEndedWhenEnteredDormant && (mPlayState != PLAY_STATE_SHUTDOWN));
 }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -238,16 +238,18 @@ public:
   // has shutdown.
   // Call on the main thread only.
   virtual bool IsEndedOrShutdown() const;
 
   // Return true if the MediaDecoderOwner's error attribute is not null.
   // If the MediaDecoder is shutting down, OwnerHasError will return true.
   bool OwnerHasError() const;
 
+  already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
+
 protected:
   // Updates the media duration. This is called while the media is being
   // played, calls before the media has reached loaded metadata are ignored.
   // The duration is assumed to be an estimate, and so a degree of
   // instability is expected; if the incoming duration is not significantly
   // different from the existing duration, the change request is ignored.
   // If the incoming duration is significantly different, the duration is
   // changed, this causes a durationchanged event to fire to the media
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -173,16 +173,20 @@ MediaFormatReader::Init()
 
   InitLayersBackendType();
 
   mAudio.mTaskQueue =
     new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
   mVideo.mTaskQueue =
     new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
 
+  // Note: GMPCrashHelper must be created on main thread, as it may use
+  // weak references, which aren't threadsafe.
+  mCrashHelper = mDecoder->GetCrashHelper();
+
   return NS_OK;
 }
 
 #ifdef MOZ_EME
 class DispatchKeyNeededEvent : public Runnable {
 public:
   DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
                          nsTArray<uint8_t>& aInitData,
@@ -406,30 +410,32 @@ MediaFormatReader::EnsureDecoderCreated(
 
   MonitorAutoLock mon(decoder.mMonitor);
 
   switch (aTrack) {
     case TrackType::kAudioTrack: {
       decoder.mDecoder = mPlatform->CreateDecoder({
         decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio,
         decoder.mTaskQueue,
-        decoder.mCallback.get()
+        decoder.mCallback.get(),
+        mCrashHelper
       });
       break;
     }
 
     case TrackType::kVideoTrack: {
       // Decoders use the layers backend to decide if they can use hardware decoding,
       // so specify LAYERS_NONE if we want to forcibly disable it.
       decoder.mDecoder = mPlatform->CreateDecoder({
         mVideo.mInfo ? *mVideo.mInfo->GetAsVideoInfo() : mInfo.mVideo,
         decoder.mTaskQueue,
         decoder.mCallback.get(),
         mLayersBackendType,
         GetImageContainer(),
+        mCrashHelper
       });
       break;
     }
     default:
       break;
   }
   if (decoder.mDecoder ) {
     decoder.mDescription = decoder.mDecoder->GetDescriptionName();
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -566,13 +566,14 @@ private:
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
 #ifdef MOZ_EME
   RefPtr<CDMProxy> mCDMProxy;
 #endif
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/TextTrack.cpp
+++ b/dom/media/TextTrack.cpp
@@ -210,27 +210,25 @@ TextTrack::UpdateActiveCueList()
       mActiveCueList->AddCue(*(*mCueList)[mCuePos]);
     }
   }
 }
 
 TextTrackCueList*
 TextTrack::GetActiveCues() {
   if (mMode != TextTrackMode::Disabled) {
-    UpdateActiveCueList();
     return mActiveCueList;
   }
   return nullptr;
 }
 
 void
 TextTrack::GetActiveCueArray(nsTArray<RefPtr<TextTrackCue> >& aCues)
 {
   if (mMode != TextTrackMode::Disabled) {
-    UpdateActiveCueList();
     mActiveCueList->GetArray(aCues);
   }
 }
 
 TextTrackReadyState
 TextTrack::ReadyState() const
 {
   return mReadyState;
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -14,16 +14,17 @@
 #include "MainThreadUtils.h"
 #include "mozilla/EMEUtils.h"
 #include "nsIConsoleService.h"
 #include "prenv.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/CDMCallbackProxy.h"
 #include "MediaData.h"
 #include "nsPrintfCString.h"
+#include "GMPService.h"
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
@@ -38,17 +39,18 @@ CDMProxy::~CDMProxy()
   MOZ_COUNT_DTOR(CDMProxy);
 }
 
 void
 CDMProxy::Init(PromiseId aPromiseId,
                const nsAString& aOrigin,
                const nsAString& aTopLevelOrigin,
                const nsAString& aGMPName,
-               bool aInPrivateBrowsing)
+               bool aInPrivateBrowsing,
+               GMPCrashHelper* aCrashHelper)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   EME_LOG("CDMProxy::Init (%s, %s) %s",
           NS_ConvertUTF16toUTF8(aOrigin).get(),
           NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
           (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
@@ -77,16 +79,17 @@ CDMProxy::Init(PromiseId aPromiseId,
   }
 
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
   data->mGMPName = aGMPName;
   data->mInPrivateBrowsing = aInPrivateBrowsing;
+  data->mCrashHelper = aCrashHelper;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<nsAutoPtr<InitData>>(this,
                                            &CDMProxy::gmp_Init,
                                            Move(data)));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 #ifdef DEBUG
@@ -221,19 +224,22 @@ CDMProxy::gmp_InitGetGMPDecryptor(nsresu
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
+  // Note: must capture helper refptr here, before the Move()
+  // when we create the GetGMPDecryptorCallback below.
+  RefPtr<GMPCrashHelper> crashHelper = Move(aData->mCrashHelper);
   UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                        Move(aData)));
-  nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
+  nsresult rv = mps->GetGMPDecryptor(crashHelper, &tags, GetNodeId(), Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -53,17 +53,18 @@ public:
 
   // Main thread only.
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             const nsAString& aGMPName,
-            bool aInPrivateBrowsing);
+            bool aInPrivateBrowsing,
+            GMPCrashHelper* aHelper);
 
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::SessionType aSessionType,
                      PromiseId aPromiseId,
@@ -183,16 +184,17 @@ private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsString mOrigin;
     nsString mTopLevelOrigin;
     nsString mGMPName;
+    RefPtr<GMPCrashHelper> mCrashHelper;
     bool mInPrivateBrowsing;
   };
 
   // GMP thread only.
   void gmp_Init(nsAutoPtr<InitData>&& aData);
   void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
   void gmp_InitGetGMPDecryptor(nsresult aResult,
                                const nsACString& aNodeId,
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -279,16 +279,36 @@ MediaKeys::ResolvePromise(PromiseId aId)
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
     promise->MaybeResolve(JS::UndefinedHandleValue);
   }
   MOZ_ASSERT(!mPromises.Contains(aId));
 }
 
+class MediaKeysGMPCrashHelper : public GMPCrashHelper
+{
+public:
+  explicit MediaKeysGMPCrashHelper(MediaKeys* aMediaKeys)
+    : mMediaKeys(aMediaKeys)
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+  }
+  already_AddRefed<nsPIDOMWindowInner>
+  GetPluginCrashedEventTarget() override
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+    EME_LOG("MediaKeysGMPCrashHelper::GetPluginCrashedEventTarget()");
+    return (mMediaKeys && mMediaKeys->GetParentObject()) ?
+      do_AddRef(mMediaKeys->GetParentObject()) : nullptr;
+  }
+private:
+  WeakPtr<MediaKeys> mMediaKeys;
+};
+
 already_AddRefed<DetailedPromise>
 MediaKeys::Init(ErrorResult& aRv)
 {
   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     NS_LITERAL_CSTRING("MediaKeys::Init()")));
   if (aRv.Failed()) {
     return nullptr;
   }
@@ -362,17 +382,18 @@ MediaKeys::Init(ErrorResult& aRv)
   // rejected.
   MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
   mCreatePromiseId = StorePromise(promise);
   AddRef();
   mProxy->Init(mCreatePromiseId,
                origin,
                topLevelOrigin,
                KeySystemToGMPName(mKeySystem),
-               inPrivateBrowsing);
+               inPrivateBrowsing,
+               new MediaKeysGMPCrashHelper(this));
 
   return promise.forget();
 }
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
@@ -385,31 +406,16 @@ MediaKeys::OnCDMCreated(PromiseId aId, c
   promise->MaybeResolve(keys);
   if (mCreatePromiseId == aId) {
     Release();
   }
 
   MediaKeySystemAccess::NotifyObservers(mParent,
                                         mKeySystem,
                                         MediaKeySystemStatus::Cdm_created);
-
-  if (aPluginId) {
-    // Prepare plugin crash reporter.
-    RefPtr<gmp::GeckoMediaPluginService> service =
-      gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
-    if (NS_WARN_IF(!service)) {
-      return;
-    }
-    if (NS_WARN_IF(!mParent)) {
-      return;
-    }
-    service->AddPluginCrashedEventTarget(aPluginId, mParent);
-    EME_LOG("MediaKeys[%p]::OnCDMCreated() registered crash handler for pluginId '%i'",
-            this, aPluginId);
-  }
 }
 
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          SessionType aSessionType,
                          ErrorResult& aRv)
 {
   if (!mProxy) {
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -14,16 +14,17 @@
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/DetailedPromise.h"
+#include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
@@ -31,25 +32,27 @@ class MediaKeySession;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
 typedef uint32_t PromiseId;
 
 // This class is used on the main thread only.
-// Note: it's addref/release is not (and can't be) thread safe!
+// Note: its addref/release is not (and can't be) thread safe!
 class MediaKeys final : public nsISupports,
-                        public nsWrapperCache
+                        public nsWrapperCache,
+                        public SupportsWeakPtr<MediaKeys>
 {
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaKeys)
 
   MediaKeys(nsPIDOMWindowInner* aParentWindow,
             const nsAString& aKeySystem, const nsAString& aCDMVersion);
 
   already_AddRefed<DetailedPromise> Init(ErrorResult& aRv);
 
   nsPIDOMWindowInner* GetParentObject() const;
 
--- a/dom/media/gmp/GMPAudioDecoderParent.cpp
+++ b/dom/media/gmp/GMPAudioDecoderParent.cpp
@@ -221,16 +221,17 @@ GMPAudioDecoderParent::ActorDestroy(Acto
     mCallback->Terminated();
     mCallback = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->AudioDecoderDestroyed(this);
     mPlugin = nullptr;
   }
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPAudioDecoderParent::RecvDecoded(const GMPAudioDecodedSampleData& aDecoded)
 {
   LOGV(("GMPAudioDecoderParent[%p]::RecvDecoded() timestamp=%lld",
         this, aDecoded.mTimeStamp()));
 
--- a/dom/media/gmp/GMPAudioDecoderParent.h
+++ b/dom/media/gmp/GMPAudioDecoderParent.h
@@ -7,24 +7,26 @@
 #define GMPAudioDecoderParent_h_
 
 #include "mozilla/RefPtr.h"
 #include "gmp-audio-decode.h"
 #include "gmp-audio-codec.h"
 #include "mozilla/gmp/PGMPAudioDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPAudioDecoderProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPAudioDecoderParent final : public GMPAudioDecoderProxy
                                   , public PGMPAudioDecoderParent
+                                  , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPAudioDecoderParent)
 
   explicit GMPAudioDecoderParent(GMPContentParent *aPlugin);
 
   nsresult Shutdown();
 
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GMPCrashHelperHolder_h_
+#define GMPCrashHelperHolder_h_
+
+#include "GMPService.h"
+#include "mozilla/RefPtr.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+class GMPCrashHelper;
+
+namespace mozilla {
+
+// Disconnecting the GMPCrashHelpers at the right time is hard. We need to
+// ensure that in the crashing case that we stay connected until the
+// "gmp-plugin-crashed" message is processed in the content process.
+//
+// We have two channels connecting to the GMP; PGMP which connects from
+// chrome to GMP process, and PGMPContent, which bridges between the content
+// and GMP process. If the GMP crashes both PGMP and PGMPContent receive
+// ActorDestroy messages and begin to shutdown at the same time.
+//
+// However the crash report mini dump must be generated in the chrome
+// process' ActorDestroy, before the "gmp-plugin-crashed" message can be sent
+// to the content process. We fire the "PluginCrashed" event when we handle
+// the "gmp-plugin-crashed" message in the content process, and we need the
+// crash helpers to do that.
+//
+// The PGMPContent's managed actors' ActorDestroy messages are the only shutdown
+// notification we get in the content process, but we can't disconnect the
+// crash handlers there in the crashing case, as ActorDestroy happens before
+// we've received the "gmp-plugin-crashed" message and had a chance for the
+// crash helpers to generate the window to dispatch PluginCrashed to initiate
+// the crash report submission notification box.
+//
+// So we need to ensure that in the content process, the GMPCrashHelpers stay
+// connected to the GMPService until after ActorDestroy messages are received
+// if there's an abnormal shutdown. In the case where the GMP doesn't crash,
+// we do actually want to disconnect GMPCrashHandlers in ActorDestroy, since
+// we don't have any other signal that a GMP actor is shutting down. If we don't
+// disconnect the crash helper there in the normal shutdown case, the helper
+// will stick around forever and leak.
+//
+// In the crashing case, the GMPCrashHelpers are deallocated when the crash
+// report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
+//
+// It's a bit yuck that we have to have two paths for disconnecting the crash
+// helpers, but there aren't really any better options.
+class GMPCrashHelperHolder
+{
+public:
+  
+  void SetCrashHelper(GMPCrashHelper* aHelper)
+  {
+    mCrashHelper = aHelper;
+  }
+  
+  GMPCrashHelper* GetCrashHelper()
+  {
+    return mCrashHelper;
+  }
+
+  void MaybeDisconnect(bool aAbnormalShutdown)
+  {
+    if (!aAbnormalShutdown) {
+      RefPtr<gmp::GeckoMediaPluginService> service(gmp::GeckoMediaPluginService::GetGeckoMediaPluginService());
+      service->DisconnectCrashHelper(GetCrashHelper());
+    }
+  }
+
+private:
+  RefPtr<GMPCrashHelper> mCrashHelper;
+};
+
+}
+
+#endif // GMPCrashHelperHolder_h_
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -421,16 +421,17 @@ GMPDecryptorParent::ActorDestroy(ActorDe
     // May call Close() (and Shutdown()) immediately or with a delay
     mCallback->Terminated();
     mCallback = nullptr;
   }
   if (mPlugin) {
     mPlugin->DecryptorDestroyed(this);
     mPlugin = nullptr;
   }
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPDecryptorParent::Recv__delete__()
 {
   LOGD(("GMPDecryptorParent[%p]::Recv__delete__()", this));
 
   if (mPlugin) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -5,27 +5,29 @@
 
 #ifndef GMPDecryptorParent_h_
 #define GMPDecryptorParent_h_
 
 #include "mozilla/gmp/PGMPDecryptorParent.h"
 #include "mozilla/RefPtr.h"
 #include "gmp-decryption.h"
 #include "GMPDecryptorProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 
 class CryptoSample;
 
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPDecryptorParent final : public GMPDecryptorProxy
                                , public PGMPDecryptorParent
+                               , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
   explicit GMPDecryptorParent(GMPContentParent *aPlugin);
 
   // GMPDecryptorProxy
 
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -29,19 +29,21 @@
 #include "mozilla/SandboxInfo.h"
 #endif
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsHashKeys.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
+#include "nsThreadUtils.h"
 
 #include "mozilla/dom/PluginCrashedEvent.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/Attributes.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 LogModule*
@@ -149,152 +151,58 @@ GeckoMediaPluginService::GeckoMediaPlugi
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 GeckoMediaPluginService::~GeckoMediaPluginService()
 {
 }
 
-void
-GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    RefPtr<GMPCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    if (!callback->IsStillValid()) {
-      LOGD(("%s::%s - Removing obsolete callback for pluginId %i",
-            __CLASS__, __FUNCTION__, callback->GetPluginId()));
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
-    }
-  }
-}
-
-GeckoMediaPluginService::GMPCrashCallback::GMPCrashCallback(const uint32_t aPluginId,
-                                                            nsPIDOMWindowInner* aParentWindow,
-                                                            nsIDocument* aDocument)
-  : mPluginId(aPluginId)
-  , mParentWindowWeakPtr(do_GetWeakReference(aParentWindow))
-  , mDocumentWeakPtr(do_GetWeakReference(aDocument))
-{
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
-void
-GeckoMediaPluginService::GMPCrashCallback::Run(const nsACString& aPluginName)
-{
-  dom::PluginCrashedEventInit init;
-  init.mPluginID = mPluginId;
-  init.mBubbles = true;
-  init.mCancelable = true;
-  init.mGmpPlugin = true;
-  CopyUTF8toUTF16(aPluginName, init.mPluginName);
-  init.mSubmittedCrashReport = false;
-
-  // The following PluginCrashedEvent fields stay empty:
-  // init.mBrowserDumpID
-  // init.mPluginFilename
-  // TODO: Can/should we fill them?
-
-  nsCOMPtr<nsPIDOMWindowInner> parentWindow;
-  nsCOMPtr<nsIDocument> document;
-  if (!GetParentWindowAndDocumentIfValid(parentWindow, document)) {
-    return;
-  }
-
-  RefPtr<dom::PluginCrashedEvent> event =
-    dom::PluginCrashedEvent::Constructor(document, NS_LITERAL_STRING("PluginCrashed"), init);
-  event->SetTrusted(true);
-  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
-
-  EventDispatcher::DispatchDOMEvent(parentWindow, nullptr, event, nullptr, nullptr);
-}
-
-bool
-GeckoMediaPluginService::GMPCrashCallback::IsStillValid()
-{
-  nsCOMPtr<nsPIDOMWindowInner> parentWindow;
-  nsCOMPtr<nsIDocument> document;
-  return GetParentWindowAndDocumentIfValid(parentWindow, document);
-}
-
-bool
-GeckoMediaPluginService::GMPCrashCallback::GetParentWindowAndDocumentIfValid(
-  nsCOMPtr<nsPIDOMWindowInner>& parentWindow,
-  nsCOMPtr<nsIDocument>& document)
-{
-  parentWindow = do_QueryReferent(mParentWindowWeakPtr);
-  if (!parentWindow) {
-    return false;
-  }
-  document = do_QueryReferent(mDocumentWeakPtr);
-  if (!document) {
-    return false;
-  }
-  nsCOMPtr<nsIDocument> parentWindowDocument = parentWindow->GetExtantDoc();
-  if (!parentWindowDocument || document.get() != parentWindowDocument.get()) {
-    return false;
-  }
-  return true;
-}
-
-void
-GeckoMediaPluginService::AddPluginCrashedEventTarget(const uint32_t aPluginId,
-                                                     nsPIDOMWindowInner* aParentWindow)
-{
-  LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
-
-  if (NS_WARN_IF(!aParentWindow)) {
-    return;
-  }
-  nsCOMPtr<nsIDocument> doc = aParentWindow->GetExtantDoc();
-  if (NS_WARN_IF(!doc)) {
-    return;
-  }
-  RefPtr<GMPCrashCallback> callback(new GMPCrashCallback(aPluginId, aParentWindow, doc));
-  RemoveObsoletePluginCrashCallbacks();
-
-  // If the plugin with that ID has already crashed without being handled,
-  // just run the handler now.
-  for (size_t i = mPluginCrashes.Length(); i != 0; --i) {
-    size_t index = i - 1;
-    const PluginCrash& crash = mPluginCrashes[index];
-    if (crash.mPluginId == aPluginId) {
-      LOGD(("%s::%s(%i) - added crash handler for crashed plugin, running handler #%u",
-        __CLASS__, __FUNCTION__, aPluginId, index));
-      callback->Run(crash.mPluginName);
-      mPluginCrashes.RemoveElementAt(index);
-    }
-  }
-
-  // Remember crash, so if a handler is added for it later, we report the
-  // crash to that window too.
-  mPluginCrashCallbacks.AppendElement(callback);
-}
-
 NS_IMETHODIMP
 GeckoMediaPluginService::RunPluginCrashCallbacks(uint32_t aPluginId,
                                                  const nsACString& aPluginName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
-  RemoveObsoletePluginCrashCallbacks();
+
+  nsAutoPtr<nsTArray<RefPtr<GMPCrashHelper>>> helpers;
+  {
+    MutexAutoLock lock(mMutex);
+    mPluginCrashHelpers.RemoveAndForget(aPluginId, helpers);
+  }
+  if (!helpers) {
+    LOGD(("%s::%s(%i) No crash helpers, not handling crash.", __CLASS__, __FUNCTION__, aPluginId));
+    return NS_OK;
+  }
 
-  for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
-    RefPtr<GMPCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    if (callback->GetPluginId() == aPluginId) {
-      LOGD(("%s::%s(%i) - Running #%u",
-          __CLASS__, __FUNCTION__, aPluginId, i - 1));
-      callback->Run(aPluginName);
-      mPluginCrashCallbacks.RemoveElementAt(i - 1);
+  for (const auto& helper : *helpers) {
+    nsCOMPtr<nsPIDOMWindowInner> window = helper->GetPluginCrashedEventTarget();
+    if (NS_WARN_IF(!window)) {
+      continue;
+    }
+    nsCOMPtr<nsIDocument> document(window->GetExtantDoc());
+    if (NS_WARN_IF(!document)) {
+      continue;
     }
-  }
-  mPluginCrashes.AppendElement(PluginCrash(aPluginId, aPluginName));
-  if (mPluginCrashes.Length() > MAX_PLUGIN_CRASHES) {
-    mPluginCrashes.RemoveElementAt(0);
+
+    dom::PluginCrashedEventInit init;
+    init.mPluginID = aPluginId;
+    init.mBubbles = true;
+    init.mCancelable = true;
+    init.mGmpPlugin = true;
+    CopyUTF8toUTF16(aPluginName, init.mPluginName);
+    init.mSubmittedCrashReport = false;
+    RefPtr<dom::PluginCrashedEvent> event =
+      dom::PluginCrashedEvent::Constructor(document,
+                                           NS_LITERAL_STRING("PluginCrashed"),
+                                           init);
+    event->SetTrusted(true);
+    event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+
+    EventDispatcher::DispatchDOMEvent(window, nullptr, event, nullptr, nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginService::Init()
 {
@@ -384,170 +292,197 @@ GeckoMediaPluginService::GetAbstractGMPT
 {
   MutexAutoLock lock(mMutex);
   return mAbstractGMPThread;
 }
 
 class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
 {
 public:
-  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
+  explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPAudioDecoderParent* gmpADP = nullptr;
-    if (aGMPParent) {
-      aGMPParent->GetGMPAudioDecoder(&gmpADP);
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPAudioDecoder(&gmpADP))) {
+      gmpADP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpADP);
   }
 
 private:
   UniquePtr<GetGMPAudioDecoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
+GeckoMediaPluginService::GetGMPAudioDecoder(GMPCrashHelper* aHelper,
+                                            nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForAudioDecoderDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                            *aTags, Move(callback))) {
+    new GetGMPContentParentForAudioDecoderDone(Move(aCallback), aHelper));
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoDecoderDone : public GetGMPContentParentCallback
 {
 public:
-  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
+  explicit GetGMPContentParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoDecoderParent* gmpVDP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
     if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoDecoder(&gmpVDP))) {
       videoHost = &gmpVDP->Host();
+      gmpVDP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVDP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoDecoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
+GeckoMediaPluginService::GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                            nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoDecoderDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                            *aTags, Move(callback))) {
+    new GetGMPContentParentForVideoDecoderDone(Move(aCallback), aHelper));
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForVideoEncoderDone : public GetGMPContentParentCallback
 {
 public:
-  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
+  explicit GetGMPContentParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback,
+                                                  GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPVideoEncoderParent* gmpVEP = nullptr;
     GMPVideoHostImpl* videoHost = nullptr;
     if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPVideoEncoder(&gmpVEP))) {
       videoHost = &gmpVEP->Host();
+      gmpVEP->SetCrashHelper(mHelper);
     }
     mCallback->Done(gmpVEP, videoHost);
   }
 
 private:
   UniquePtr<GetGMPVideoEncoderCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
+GeckoMediaPluginService::GetGMPVideoEncoder(GMPCrashHelper* aHelper,
+                                            nsTArray<nsCString>* aTags,
                                             const nsACString& aNodeId,
                                             UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForVideoEncoderDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
-                            *aTags, Move(callback))) {
+    new GetGMPContentParentForVideoEncoderDone(Move(aCallback), aHelper));
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_VIDEO_ENCODER),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 class GetGMPContentParentForDecryptorDone : public GetGMPContentParentCallback
 {
 public:
-  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
+  explicit GetGMPContentParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback,
+                                               GMPCrashHelper* aHelper)
    : mCallback(Move(aCallback))
+   , mHelper(aHelper)
   {
   }
 
   void Done(GMPContentParent* aGMPParent) override
   {
     GMPDecryptorParent* ksp = nullptr;
-    if (aGMPParent) {
-      aGMPParent->GetGMPDecryptor(&ksp);
+    if (aGMPParent && NS_SUCCEEDED(aGMPParent->GetGMPDecryptor(&ksp))) {
+      ksp->SetCrashHelper(mHelper);
     }
     mCallback->Done(ksp);
   }
 
 private:
   UniquePtr<GetGMPDecryptorCallback> mCallback;
+  RefPtr<GMPCrashHelper> mHelper;
 };
 
 NS_IMETHODIMP
-GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
+GeckoMediaPluginService::GetGMPDecryptor(GMPCrashHelper* aHelper,
+                                         nsTArray<nsCString>* aTags,
                                          const nsACString& aNodeId,
                                          UniquePtr<GetGMPDecryptorCallback>&& aCallback)
 {
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
   if (!SandboxInfo::Get().CanSandboxMedia()) {
     NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
                "EME decryption not available without sandboxing support.");
     return NS_ERROR_NOT_AVAILABLE;
@@ -558,28 +493,81 @@ GeckoMediaPluginService::GetGMPDecryptor
   NS_ENSURE_ARG(aTags && aTags->Length() > 0);
   NS_ENSURE_ARG(aCallback);
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   UniquePtr<GetGMPContentParentCallback> callback(
-    new GetGMPContentParentForDecryptorDone(Move(aCallback)));
-  if (!GetContentParentFrom(aNodeId, NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                            *aTags, Move(callback))) {
+    new GetGMPContentParentForDecryptorDone(Move(aCallback), aHelper));
+  if (!GetContentParentFrom(aHelper,
+                            aNodeId,
+                            NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
+                            *aTags,
+                            Move(callback))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginService::HasPluginForAPI(const nsACString& aAPI,
                                          nsTArray<nsCString>* aTags,
                                          bool* aOutHavePlugin)
 {
   nsCString unused;
   return GetPluginVersionForAPI(aAPI, aTags, aOutHavePlugin, unused);
 }
 
+void
+GeckoMediaPluginService::ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper)
+{
+  if (!aHelper) {
+    return;
+  }
+  MutexAutoLock lock(mMutex);
+  nsTArray<RefPtr<GMPCrashHelper>>* helpers;
+  if (!mPluginCrashHelpers.Get(aPluginId, &helpers)) {
+    helpers = new nsTArray<RefPtr<GMPCrashHelper>>();
+    mPluginCrashHelpers.Put(aPluginId, helpers);
+  } else if (helpers->Contains(aHelper)) {
+    return;
+  }
+  helpers->AppendElement(aHelper);
+}
+
+void GeckoMediaPluginService::DisconnectCrashHelper(GMPCrashHelper* aHelper)
+{
+  if (!aHelper) {
+    return;
+  }
+  MutexAutoLock lock(mMutex);
+  for (auto iter = mPluginCrashHelpers.Iter(); !iter.Done(); iter.Next()) {
+    nsTArray<RefPtr<GMPCrashHelper>>* helpers = iter.Data();
+    if (!helpers->Contains(aHelper)) {
+      continue;
+    }
+    helpers->RemoveElement(aHelper);
+    MOZ_ASSERT(!helpers->Contains(aHelper)); // Ensure there aren't duplicates.
+    if (helpers->IsEmpty()) {
+      iter.Remove();
+    }
+  }
+}
+
 } // namespace gmp
 } // namespace mozilla
+
+NS_IMPL_ADDREF(GMPCrashHelper)
+NS_IMPL_RELEASE_WITH_DESTROY(GMPCrashHelper, Destroy())
+
+void
+GMPCrashHelper::Destroy()
+{
+  if (NS_IsMainThread()) {
+    delete this;
+  } else {
+    // Don't addref, as then we'd end up releasing after the detele runs!
+    NS_DispatchToMainThread(mozilla::NewNonOwningRunnableMethod(this, &GMPCrashHelper::Destroy));
+  }
+}
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -15,19 +15,45 @@
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
 #include "mozilla/AbstractThread.h"
+#include "nsClassHashtable.h"
+#include "nsISupportsImpl.h"
 
 template <class> struct already_AddRefed;
 
+// For every GMP actor requested, the caller can specify a crash helper,
+// which is an object which supplies the nsPIDOMWindowInner to which we'll
+// dispatch the PluginCrashed event if the GMP crashes.
+// GMPCrashHelper has threadsafe refcounting. Its release method ensures
+// that instances are destroyed on the main thread.
+class GMPCrashHelper
+{
+public:
+  NS_METHOD_(MozExternalRefCountType) AddRef(void);
+  NS_METHOD_(MozExternalRefCountType) Release(void);
+
+  // Called on the main thread.
+  virtual already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() = 0;
+
+protected:
+  virtual ~GMPCrashHelper()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+  void Destroy();
+  mozilla::ThreadSafeAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+};
+
 namespace mozilla {
 
 extern LogModule* GetGMPLog();
 
 namespace gmp {
 
 class GetGMPContentParentCallback;
 
@@ -40,109 +66,68 @@ public:
   virtual nsresult Init();
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetThread(nsIThread** aThread) override;
   NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, nsTArray<nsCString>* aTags,
                              bool *aRetVal) override;
-  NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPVideoDecoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPVideoEncoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPAudioDecoder(GMPCrashHelper* aHelper,
+                                nsTArray<nsCString>* aTags,
                                 const nsACString& aNodeId,
                                 UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
     override;
-  NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
+  NS_IMETHOD GetGMPDecryptor(GMPCrashHelper* aHelper,
+                             nsTArray<nsCString>* aTags,
                              const nsACString& aNodeId,
                              UniquePtr<GetGMPDecryptorCallback>&& aCallback)
     override;
 
   int32_t AsyncShutdownTimeoutMs();
 
   NS_IMETHOD RunPluginCrashCallbacks(uint32_t aPluginId,
                                      const nsACString& aPluginName) override;
 
-  // Sets the window to which 'PluginCrashed' chromeonly event is dispatched.
-  // Note: if the plugin has crashed before the target window has been set,
-  // the 'PluginCrashed' event is dispatched as soon as a target window is set.
-  void AddPluginCrashedEventTarget(const uint32_t aPluginId,
-                                   nsPIDOMWindowInner* aParentWindow);
+  RefPtr<AbstractThread> GetAbstractGMPThread();
 
-  RefPtr<AbstractThread> GetAbstractGMPThread();
+  void ConnectCrashHelper(uint32_t aPluginId, GMPCrashHelper* aHelper);
+  void DisconnectCrashHelper(GMPCrashHelper* aHelper);
 
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
-  void RemoveObsoletePluginCrashCallbacks(); // Called from add/run.
-
   virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
-  virtual bool GetContentParentFrom(const nsACString& aNodeId,
+  virtual bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                                    const nsACString& aNodeId,
                                     const nsCString& aAPI,
                                     const nsTArray<nsCString>& aTags,
                                     UniquePtr<GetGMPContentParentCallback>&& aCallback) = 0;
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
   nsresult GMPDispatch(already_AddRefed<nsIRunnable> event, uint32_t flags = NS_DISPATCH_NORMAL);
   void ShutdownGMPThread();
 
-  Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread and
+  Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread, mPluginCrashHelpers,
                 // mGMPThreadShutdown and some members in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
   RefPtr<AbstractThread> mAbstractGMPThread;
   bool mGMPThreadShutdown;
   bool mShuttingDownOnGMPThread;
 
-  class GMPCrashCallback
-  {
-  public:
-    NS_INLINE_DECL_REFCOUNTING(GMPCrashCallback)
-
-    GMPCrashCallback(const uint32_t aPluginId,
-                     nsPIDOMWindowInner* aParentWindow,
-                     nsIDocument* aDocument);
-    void Run(const nsACString& aPluginName);
-    bool IsStillValid();
-    uint32_t GetPluginId() const { return mPluginId; }
-  private:
-    virtual ~GMPCrashCallback() { MOZ_ASSERT(NS_IsMainThread()); }
-
-    bool GetParentWindowAndDocumentIfValid(nsCOMPtr<nsPIDOMWindowInner>& parentWindow,
-                                           nsCOMPtr<nsIDocument>& document);
-    const uint32_t mPluginId;
-    nsWeakPtr mParentWindowWeakPtr;
-    nsWeakPtr mDocumentWeakPtr;
-  };
-
-  struct PluginCrash
-  {
-    PluginCrash(uint32_t aPluginId,
-                const nsACString& aPluginName)
-      : mPluginId(aPluginId)
-      , mPluginName(aPluginName)
-    {
-    }
-    uint32_t mPluginId;
-    nsCString mPluginName;
-
-    bool operator==(const PluginCrash& aOther) const {
-      return mPluginId == aOther.mPluginId &&
-             mPluginName == aOther.mPluginName;
-    }
-  };
-
-  static const size_t MAX_PLUGIN_CRASHES = 100;
-  nsTArray<PluginCrash> mPluginCrashes;
-
-  nsTArray<RefPtr<GMPCrashCallback>> mPluginCrashCallbacks;
+  nsClassHashtable<nsUint32HashKey, nsTArray<RefPtr<GMPCrashHelper>>> mPluginCrashHelpers;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPService_h_
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -45,43 +45,54 @@ GeckoMediaPluginServiceChild::GetSinglet
   }
 #endif
   return service.forget().downcast<GeckoMediaPluginServiceChild>();
 }
 
 class GetContentParentFromDone : public GetServiceChildCallback
 {
 public:
-  GetContentParentFromDone(const nsACString& aNodeId, const nsCString& aAPI,
+  GetContentParentFromDone(GMPCrashHelper* aHelper, const nsACString& aNodeId, const nsCString& aAPI,
                            const nsTArray<nsCString>& aTags,
                            UniquePtr<GetGMPContentParentCallback>&& aCallback)
-    : mNodeId(aNodeId),
+    : mHelper(aHelper),
+      mNodeId(aNodeId),
       mAPI(aAPI),
       mTags(aTags),
       mCallback(Move(aCallback))
   {
   }
 
   void Done(GMPServiceChild* aGMPServiceChild) override
   {
     if (!aGMPServiceChild) {
       mCallback->Done(nullptr);
       return;
     }
 
+    uint32_t pluginId;
+    nsresult rv;
+    bool ok = aGMPServiceChild->SendSelectGMP(mNodeId, mAPI, mTags, &pluginId, &rv);
+    if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
+      mCallback->Done(nullptr);
+      return;
+    }
+
+    if (mHelper) {
+      RefPtr<GeckoMediaPluginService> gmps(GeckoMediaPluginService::GetGeckoMediaPluginService());
+      gmps->ConnectCrashHelper(pluginId, mHelper);
+    }
+
     nsTArray<base::ProcessId> alreadyBridgedTo;
     aGMPServiceChild->GetAlreadyBridgedTo(alreadyBridgedTo);
 
     base::ProcessId otherProcess;
     nsCString displayName;
-    uint32_t pluginId;
-    nsresult rv;
-    bool ok = aGMPServiceChild->SendLoadGMP(mNodeId, mAPI, mTags,
-                                            alreadyBridgedTo, &otherProcess,
-                                            &displayName, &pluginId, &rv);
+    ok = aGMPServiceChild->SendLaunchGMP(pluginId, alreadyBridgedTo, &otherProcess,
+                                         &displayName, &rv);
     if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
       mCallback->Done(nullptr);
       return;
     }
 
     RefPtr<GMPContentParent> parent;
     aGMPServiceChild->GetBridgedGMPContentParent(otherProcess,
                                                  getter_AddRefs(parent));
@@ -89,32 +100,34 @@ public:
       parent->SetDisplayName(displayName);
       parent->SetPluginId(pluginId);
     }
 
     mCallback->Done(parent);
   }
 
 private:
+  RefPtr<GMPCrashHelper> mHelper;
   nsCString mNodeId;
   nsCString mAPI;
   const nsTArray<nsCString> mTags;
   UniquePtr<GetGMPContentParentCallback> mCallback;
 };
 
 bool
-GeckoMediaPluginServiceChild::GetContentParentFrom(const nsACString& aNodeId,
+GeckoMediaPluginServiceChild::GetContentParentFrom(GMPCrashHelper* aHelper,
+                                                   const nsACString& aNodeId,
                                                    const nsCString& aAPI,
                                                    const nsTArray<nsCString>& aTags,
                                                    UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   UniquePtr<GetServiceChildCallback> callback(
-    new GetContentParentFromDone(aNodeId, aAPI, aTags, Move(aCallback)));
+    new GetContentParentFromDone(aHelper, aNodeId, aAPI, aTags, Move(aCallback)));
   GetServiceChild(Move(callback));
 
   return true;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::GetPluginVersionForAPI(const nsACString& aAPI,
                                                      nsTArray<nsCString>* aTags,
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -55,17 +55,18 @@ public:
 
   void RemoveGMPContentParent(GMPContentParent* aGMPContentParent);
 
 protected:
   void InitializePlugins(AbstractThread*) override
   {
     // Nothing to do here.
   }
-  bool GetContentParentFrom(const nsACString& aNodeId,
+  bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                            const nsACString& aNodeId,
                             const nsCString& aAPI,
                             const nsTArray<nsCString>& aTags,
                             UniquePtr<GetGMPContentParentCallback>&& aCallback)
     override;
 
 private:
   friend class OpenPGMPServiceChild;
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -520,41 +520,44 @@ GeckoMediaPluginServiceParent::EnsureIni
     return GenericPromise::CreateAndResolve(true, __func__);
   }
   // We should have an init promise in flight.
   MOZ_ASSERT(!mInitPromise.IsEmpty());
   return mInitPromise.Ensure(__func__);
 }
 
 bool
-GeckoMediaPluginServiceParent::GetContentParentFrom(const nsACString& aNodeId,
+GeckoMediaPluginServiceParent::GetContentParentFrom(GMPCrashHelper* aHelper,
+                                                    const nsACString& aNodeId,
                                                     const nsCString& aAPI,
                                                     const nsTArray<nsCString>& aTags,
                                                     UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   if (!thread) {
     return false;
   }
 
   RefPtr<GeckoMediaPluginServiceParent> self(this);
   nsCString nodeId(aNodeId);
   nsTArray<nsCString> tags(aTags);
   nsCString api(aAPI);
   GetGMPContentParentCallback* rawCallback = aCallback.release();
+  RefPtr<GMPCrashHelper> helper(aHelper);
   EnsureInitialized()->Then(thread, __func__,
-    [self, tags, api, nodeId, rawCallback]() -> void {
+    [self, tags, api, nodeId, rawCallback, helper]() -> void {
       UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
       RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeId, api, tags);
       LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)self, (void *)gmp, api.get()));
       if (!gmp) {
         NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
         callback->Done(nullptr);
         return;
       }
+      self->ConnectCrashHelper(gmp->GetPluginId(), helper);
       gmp->GetGMPContentParent(Move(callback));
     },
     [rawCallback]() -> void {
       UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
       NS_WARNING("GMPService::EnsureInitialized failed.");
       callback->Done(nullptr);
     });
   return true;
@@ -1813,51 +1816,86 @@ GeckoMediaPluginServiceParent::ClearStor
   }
 
   // Clear private-browsing storage.
   mTempGMPStorage.Clear();
 
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
+already_AddRefed<GMPParent>
+GeckoMediaPluginServiceParent::GetById(uint32_t aPluginId)
+{
+  MutexAutoLock lock(mMutex);
+  for (const RefPtr<GMPParent>& gmp : mPlugins) {
+    if (gmp->GetPluginId() == aPluginId) {
+      return do_AddRef(gmp);
+    }
+  }
+  return nullptr;
+}
+
 GMPServiceParent::~GMPServiceParent()
 {
   RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
   XRE_GetIOMessageLoop()->PostTask(task.forget());
 }
 
 bool
-GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
-                              const nsCString& aAPI,
-                              nsTArray<nsCString>&& aTags,
-                              nsTArray<ProcessId>&& aAlreadyBridgedTo,
-                              ProcessId* aId,
-                              nsCString* aDisplayName,
-                              uint32_t* aPluginId,
-                              nsresult* aRv)
+GMPServiceParent::RecvSelectGMP(const nsCString& aNodeId,
+                                const nsCString& aAPI,
+                                nsTArray<nsCString>&& aTags,
+                                uint32_t* aOutPluginId,
+                                nsresult* aOutRv)
 {
-  *aRv = NS_OK;
   if (mService->IsShuttingDown()) {
-    *aRv = NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+    *aOutRv = NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
     return true;
   }
 
   RefPtr<GMPParent> gmp = mService->SelectPluginForAPI(aNodeId, aAPI, aTags);
+  if (gmp) {
+    *aOutPluginId = gmp->GetPluginId();
+    *aOutRv = NS_OK;
+  } else {
+    *aOutRv = NS_ERROR_FAILURE;
+  }
 
   nsCString api = aTags[0];
   LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
 
-  if (!gmp || !gmp->EnsureProcessLoaded(aId)) {
+  return true;
+}
+
+bool
+GMPServiceParent::RecvLaunchGMP(const uint32_t& aPluginId,
+                                nsTArray<ProcessId>&& aAlreadyBridgedTo,
+                                ProcessId* aOutProcessId,
+                                nsCString* aOutDisplayName,
+                                nsresult* aOutRv)
+{
+  *aOutRv = NS_OK;
+  if (mService->IsShuttingDown()) {
+    *aOutRv = NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+    return true;
+  }
+
+  RefPtr<GMPParent> gmp(mService->GetById(aPluginId));
+  if (!gmp) {
+    *aOutRv = NS_ERROR_FAILURE;
+    return true;
+  }
+
+  if (!gmp->EnsureProcessLoaded(aOutProcessId)) {
     return false;
   }
 
-  *aDisplayName = gmp->GetDisplayName();
-  *aPluginId = gmp->GetPluginId();
+  *aOutDisplayName = gmp->GetDisplayName();
 
-  return aAlreadyBridgedTo.Contains(*aId) || gmp->Bridge(this);
+  return aAlreadyBridgedTo.Contains(*aOutProcessId) || gmp->Bridge(this);
 }
 
 bool
 GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
                                    const nsString& aTopLevelOrigin,
                                    const nsString& aGMPName,
                                    const bool& aInPrivateBrowsing,
                                    nsCString* aID)
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -101,24 +101,27 @@ private:
     ~DirectoryFilter() {}
   };
   void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
   void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
                             DirectoryFilter& aFilter);
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
   void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
+  already_AddRefed<GMPParent> GetById(uint32_t aPluginId);
+
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   void InitializePlugins(AbstractThread* aAbstractGMPThread) override;
   RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
   RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
-  bool GetContentParentFrom(const nsACString& aNodeId,
+  bool GetContentParentFrom(GMPCrashHelper* aHelper,
+                            const nsACString& aNodeId,
                             const nsCString& aAPI,
                             const nsTArray<nsCString>& aTags,
                             UniquePtr<GetGMPContentParentCallback>&& aCallback)
     override;
 private:
   // Creates a copy of aOriginal. Note that the caller is responsible for
   // adding this to GeckoMediaPluginServiceParent::mPlugins.
   already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
@@ -220,37 +223,41 @@ class GMPServiceParent final : public PG
 {
 public:
   explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
     : mService(aService)
   {
   }
   virtual ~GMPServiceParent();
 
-  bool RecvLoadGMP(const nsCString& aNodeId,
-                   const nsCString& aApi,
-                   nsTArray<nsCString>&& aTags,
-                   nsTArray<ProcessId>&& aAlreadyBridgedTo,
-                   base::ProcessId* aID,
-                   nsCString* aDisplayName,
-                   uint32_t* aPluginId,
-                   nsresult* aRv) override;
   bool RecvGetGMPNodeId(const nsString& aOrigin,
                         const nsString& aTopLevelOrigin,
                         const nsString& aGMPName,
                         const bool& aInPrivateBrowsing,
                         nsCString* aID) override;
   static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
                                             nsTArray<nsCString>&& aTags,
                                             bool* aHasPlugin,
                                             nsCString* aVersion);
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static PGMPServiceParent* Create(Transport* aTransport, ProcessId aOtherPid);
 
+  bool RecvSelectGMP(const nsCString& aNodeId,
+                     const nsCString& aAPI,
+                     nsTArray<nsCString>&& aTags,
+                     uint32_t* aOutPluginId,
+                     nsresult* aOutRv) override;
+
+  bool RecvLaunchGMP(const uint32_t& aPluginId,
+                     nsTArray<ProcessId>&& aAlreadyBridgedTo,
+                     ProcessId* aOutID,
+                     nsCString* aOutDisplayName,
+                     nsresult* aOutRv) override;
+
 private:
   RefPtr<GeckoMediaPluginServiceParent> mService;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPServiceParent_h_
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -295,16 +295,17 @@ GMPVideoDecoderParent::ActorDestroy(Acto
     mCallback = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoDecoderDestroyed(this);
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed();
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 bool
 GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
 {
   --mFrameCount;
   LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld frameCount=%d",
     this, aDecodedFrame.mTimestamp(), mFrameCount));
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -10,25 +10,27 @@
 #include "gmp-video-decode.h"
 #include "mozilla/gmp/PGMPVideoDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoDecoderProxy.h"
 #include "VideoUtils.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPVideoDecoderParent final : public PGMPVideoDecoderParent
                                   , public GMPVideoDecoderProxy
                                   , public GMPSharedMemManager
+                                  , public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
 
   explicit GMPVideoDecoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   nsresult Shutdown();
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp
+++ b/dom/media/gmp/GMPVideoEncoderParent.cpp
@@ -264,16 +264,17 @@ GMPVideoEncoderParent::ActorDestroy(Acto
     mEncodedThread = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoEncoderDestroyed(this);
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed(); // same as DoneWithAPI
+  MaybeDisconnect(aWhy == AbnormalShutdown);
 }
 
 static void
 EncodedCallback(GMPVideoEncoderCallbackProxy* aCallback,
                 GMPVideoEncodedFrame* aEncodedFrame,
                 nsTArray<uint8_t>* aCodecSpecificInfo,
                 nsCOMPtr<nsIThread> aThread)
 {
--- a/dom/media/gmp/GMPVideoEncoderParent.h
+++ b/dom/media/gmp/GMPVideoEncoderParent.h
@@ -9,25 +9,27 @@
 #include "mozilla/RefPtr.h"
 #include "gmp-video-encode.h"
 #include "mozilla/gmp/PGMPVideoEncoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoEncoderProxy.h"
+#include "GMPCrashHelperHolder.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPVideoEncoderParent : public GMPVideoEncoderProxy,
                               public PGMPVideoEncoderParent,
-                              public GMPSharedMemManager
+                              public GMPSharedMemManager,
+                              public GMPCrashHelperHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent)
 
   explicit GMPVideoEncoderParent(GMPContentParent *aPlugin);
 
   GMPVideoHostImpl& Host();
   void Shutdown();
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -10,20 +10,23 @@ using base::ProcessId from "base/process
 namespace mozilla {
 namespace gmp {
 
 sync protocol PGMPService
 {
   parent spawns PGMP as child;
 
 parent:
-  sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags,
-               ProcessId[] alreadyBridgedTo)
-    returns (ProcessId id, nsCString displayName, uint32_t pluginId,
-             nsresult aResult);
+
+  sync SelectGMP(nsCString nodeId, nsCString api, nsCString[] tags)
+    returns (uint32_t pluginId, nsresult aResult);
+
+  sync LaunchGMP(uint32_t pluginId, ProcessId[] alreadyBridgedTo)
+    returns (ProcessId id, nsCString displayName, nsresult aResult);
+
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
                     nsString gmpName,
                     bool inPrivateBrowsing)
     returns (nsCString id);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -33,16 +33,17 @@ EXPORTS += [
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
     'GMPChild.h',
     'GMPContentChild.h',
     'GMPContentParent.h',
+    'GMPCrashHelperHolder.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
     'GMPLoader.h',
     'GMPMessageUtils.h',
     'GMPParent.h',
     'GMPPlatform.h',
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -10,16 +10,17 @@
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "nsStringGlue.h"
 class GMPAudioDecoderProxy;
 class GMPDecryptorProxy;
 class GMPVideoDecoderProxy;
 class GMPVideoEncoderProxy;
 class GMPVideoHost;
+class GMPCrashHelper;
 
 template<class T>
 class GMPGetterCallback
 {
 public:
   GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
   virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
   virtual void Done(T*) = 0;
@@ -46,16 +47,17 @@ public:
 %}
 
 [ptr] native TagArray(nsTArray<nsCString>);
 native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
 native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
 native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
 native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
 native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
+native GMPCrashHelperPtr(GMPCrashHelper*);
 
 [scriptable, uuid(44d362ae-937a-4803-bee6-f2512a0149d1)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
@@ -89,65 +91,70 @@ interface mozIGeckoMediaPluginService : 
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
    * callback object will not be called at all.
    */
   [noscript]
-  void getGMPVideoDecoder(in TagArray tags,
+  void getGMPVideoDecoder(in GMPCrashHelperPtr helper,
+                          in TagArray tags,
                           [optional] in ACString nodeId,
                           in GetGMPVideoDecoderCallback callback);
 
   /**
    * Get a video encoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags.
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
    * callback object will not be called at all.
    */
   [noscript]
-  void getGMPVideoEncoder(in TagArray tags,
+  void getGMPVideoEncoder(in GMPCrashHelperPtr helper,
+                          in TagArray tags,
                           [optional] in ACString nodeId,
                           in GetGMPVideoEncoderCallback callback);
 
   /**
    * Returns an audio decoder that supports the specified tags.
    * The array of tags should at least contain a codec tag, and optionally
    * other tags such as for EME keysystem.
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
    * callback object will not be called at all.
    */
   [noscript]
-  void getGMPAudioDecoder(in TagArray tags,
+  void getGMPAudioDecoder(in GMPCrashHelperPtr helper,
+                          in TagArray tags,
                           [optional] in ACString nodeId,
                           in GetGMPAudioDecoderCallback callback);
 
   /**
    * Returns a decryption session manager that supports the specified tags.
    * The array of tags should at least contain a key system tag, and optionally
    * other tags.
    * Callable only on GMP thread.
    * This is an asynchronous operation, the Done method of the callback object
    * will be called on the GMP thread with the result (which might be null in
    * the case of failure). This method always takes ownership of the callback
    * object, but if this method returns an error then the Done method of the
    * callback object will not be called at all.
    */
   [noscript]
-  void getGMPDecryptor(in TagArray tags, in ACString nodeId,
+  void getGMPDecryptor(in GMPCrashHelperPtr helper,
+                       in TagArray tags,
+                       in ACString nodeId,
                        in GetGMPDecryptorCallback callback);
 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
   [noscript]
   void getNodeId(in AString origin,
                  in AString topLevelOrigin,
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -46,17 +46,18 @@ struct GMPTestRunner
   void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
   void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
 
 private:
   ~GMPTestRunner() { }
 };
 
 template<class T, class Base,
-         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
+         nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(GMPCrashHelper*,
+                                                                nsTArray<nsCString>*,
                                                                 const nsACString&,
                                                                 UniquePtr<Base>&&)>
 class RunTestGMPVideoCodec : public Base
 {
 public:
   void Done(T* aGMP, GMPVideoHost* aHost) override
   {
     EXPECT_TRUE(aGMP);
@@ -85,17 +86,17 @@ protected:
   static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
   {
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("h264"));
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     RefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
-    return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
+    return ((*service).*Getter)(nullptr, &tags, aNodeId, Move(aCallback));
   }
 
 protected:
   GMPTestMonitor& mMonitor;
 };
 
 typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
                              GetGMPVideoDecoderCallback,
@@ -673,17 +674,17 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     UniquePtr<GetGMPDecryptorCallback> callback(
       new CreateDecryptorDone(this, aContinuation));
     nsresult rv =
-      service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
+      service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     RefPtr<GeckoMediaPluginService> service =
--- a/dom/media/gtest/TestGMPRemoveAndDelete.cpp
+++ b/dom/media/gtest/TestGMPRemoveAndDelete.cpp
@@ -318,17 +318,17 @@ GMPRemoveTest::gmp_GetVideoDecoder(nsCSt
     GMPTestMonitor* mMonitor;
     GMPVideoDecoderProxy** mDecoder;
     GMPVideoHost** mHost;
   };
 
   UniquePtr<GetGMPVideoDecoderCallback>
     cb(new Callback(&mTestMonitor, aOutDecoder, aOutHost));
 
-  if (NS_FAILED(GetService()->GetGMPVideoDecoder(&tags, aNodeId, Move(cb)))) {
+  if (NS_FAILED(GetService()->GetGMPVideoDecoder(nullptr, &tags, aNodeId, Move(cb)))) {
     mTestMonitor.SetFinished();
   }
 }
 
 void
 GMPRemoveTest::CloseVideoDecoder()
 {
   mGMPThread->Dispatch(
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -7,16 +7,17 @@
 #if !defined(PlatformDecoderModule_h_)
 #define PlatformDecoderModule_h_
 
 #include "MediaDecoderReader.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
+#include "GMPService.h"
 #include <queue>
 
 namespace mozilla {
 class TrackInfo;
 class AudioInfo;
 class VideoInfo;
 class MediaRawData;
 class DecoderDoctorDiagnostics;
@@ -57,23 +58,25 @@ struct CreateDecoderParams {
   }
 
   const TrackInfo& mConfig;
   TaskQueue* mTaskQueue = nullptr;
   MediaDataDecoderCallback* mCallback = nullptr;
   DecoderDoctorDiagnostics* mDiagnostics = nullptr;
   layers::ImageContainer* mImageContainer = nullptr;
   layers::LayersBackend mLayersBackend = layers::LayersBackend::LAYERS_NONE;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 
 private:
   void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
   void Set(MediaDataDecoderCallback* aCallback) { mCallback = aCallback; }
   void Set(DecoderDoctorDiagnostics* aDiagnostics) { mDiagnostics = aDiagnostics; }
   void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; }
   void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; }
+  void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
   template <typename T1, typename T2, typename... Ts>
   void Set(T1 a1, T2 a2, Ts... as)
   {
     // Parameter pack expansion trick, to call Set() on each argument.
     using expander = int[];
     (void)expander {
       (Set(a1), 0), (Set(a2), 0), (Set(as), 0)...
     };
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
@@ -126,16 +126,17 @@ AudioCallbackAdapter::Terminated()
   mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
 }
 
 GMPAudioDecoderParams::GMPAudioDecoderParams(const CreateDecoderParams& aParams)
   : mConfig(aParams.AudioConfig())
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(nullptr)
   , mAdapter(nullptr)
+  , mCrashHelper(aParams.mCrashHelper)
 {}
 
 GMPAudioDecoderParams&
 GMPAudioDecoderParams::WithCallback(MediaDataDecoderProxy* aWrapper)
 {
   MOZ_ASSERT(aWrapper);
   MOZ_ASSERT(!mCallback); // Should only be called once per instance.
   mCallback = aWrapper->Callback();
@@ -153,16 +154,17 @@ GMPAudioDecoderParams::WithAdapter(Audio
   return *this;
 }
 
 GMPAudioDecoder::GMPAudioDecoder(const GMPAudioDecoderParams& aParams)
   : mConfig(aParams.mConfig)
   , mCallback(aParams.mCallback)
   , mGMP(nullptr)
   , mAdapter(aParams.mAdapter)
+  , mCrashHelper(aParams.mCrashHelper)
 {
   MOZ_ASSERT(!mAdapter || mCallback == mAdapter->Callback());
   if (!mAdapter) {
     mAdapter = new AudioCallbackAdapter(mCallback);
   }
 }
 
 void
@@ -225,17 +227,17 @@ GMPAudioDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPAudioDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPAudioDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPAudioDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
@@ -52,16 +52,17 @@ struct GMPAudioDecoderParams {
   explicit GMPAudioDecoderParams(const CreateDecoderParams& aParams);
   GMPAudioDecoderParams& WithCallback(MediaDataDecoderProxy* aWrapper);
   GMPAudioDecoderParams& WithAdapter(AudioCallbackAdapter* aAdapter);
 
   const AudioInfo& mConfig;
   TaskQueue* mTaskQueue;
   MediaDataDecoderCallbackProxy* mCallback;
   AudioCallbackAdapter* mAdapter;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 class GMPAudioDecoder : public MediaDataDecoder {
 public:
   explicit GMPAudioDecoder(const GMPAudioDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
@@ -98,13 +99,14 @@ private:
   void GMPInitDone(GMPAudioDecoderProxy* aGMP);
 
   const AudioInfo mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPAudioDecoderProxy* mGMP;
   nsAutoPtr<AudioCallbackAdapter> mAdapter;
   MozPromiseHolder<InitPromise> mInitPromise;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif // GMPAudioDecoder_h_
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -106,16 +106,17 @@ VideoCallbackAdapter::Terminated()
 
 GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
   : mConfig(aParams.VideoConfig())
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(nullptr)
   , mAdapter(nullptr)
   , mImageContainer(aParams.mImageContainer)
   , mLayersBackend(aParams.mLayersBackend)
+  , mCrashHelper(aParams.mCrashHelper)
 {}
 
 GMPVideoDecoderParams&
 GMPVideoDecoderParams::WithCallback(MediaDataDecoderProxy* aWrapper)
 {
   MOZ_ASSERT(aWrapper);
   MOZ_ASSERT(!mCallback); // Should only be called once per instance.
   mCallback = aWrapper->Callback();
@@ -135,16 +136,17 @@ GMPVideoDecoderParams::WithAdapter(Video
 
 GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
   : mConfig(aParams.mConfig)
   , mCallback(aParams.mCallback)
   , mGMP(nullptr)
   , mHost(nullptr)
   , mAdapter(aParams.mAdapter)
   , mConvertNALUnitLengths(false)
+  , mCrashHelper(aParams.mCrashHelper)
 {
   MOZ_ASSERT(!mAdapter || mCallback == mAdapter->Callback());
   if (!mAdapter) {
     mAdapter = new VideoCallbackAdapter(mCallback,
                                         VideoInfo(mConfig.mDisplay.width,
                                                   mConfig.mDisplay.height),
                                         aParams.mImageContainer);
   }
@@ -276,17 +278,17 @@ GMPVideoDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPVideoDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPVideoDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -57,16 +57,17 @@ struct GMPVideoDecoderParams {
   GMPVideoDecoderParams& WithAdapter(VideoCallbackAdapter* aAdapter);
 
   const VideoInfo& mConfig;
   TaskQueue* mTaskQueue;
   MediaDataDecoderCallbackProxy* mCallback;
   VideoCallbackAdapter* mAdapter;
   layers::ImageContainer* mImageContainer;
   layers::LayersBackend mLayersBackend;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 class GMPVideoDecoder : public MediaDataDecoder {
 public:
   explicit GMPVideoDecoder(const GMPVideoDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
@@ -106,13 +107,14 @@ private:
   const VideoInfo mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
   nsAutoPtr<VideoCallbackAdapter> mAdapter;
   bool mConvertNALUnitLengths;
   MozPromiseHolder<InitPromise> mInitPromise;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif // GMPVideoDecoder_h_
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -20,16 +20,17 @@ H264Converter::H264Converter(PlatformDec
   : mPDM(aPDM)
   , mOriginalConfig(aParams.VideoConfig())
   , mCurrentConfig(aParams.VideoConfig())
   , mLayersBackend(aParams.mLayersBackend)
   , mImageContainer(aParams.mImageContainer)
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(aParams.mCallback)
   , mDecoder(nullptr)
+  , mGMPCrashHelper(aParams.mCrashHelper)
   , mNeedAVCC(aPDM->DecoderNeedsConversion(aParams.mConfig) == PlatformDecoderModule::kNeedAVCC)
   , mLastError(NS_OK)
 {
   CreateDecoder(aParams.mDiagnostics);
 }
 
 H264Converter::~H264Converter()
 {
@@ -142,17 +143,18 @@ H264Converter::CreateDecoder(DecoderDoct
   }
 
   mDecoder = mPDM->CreateVideoDecoder({
     mNeedAVCC ? mCurrentConfig : mOriginalConfig,
     mTaskQueue,
     mCallback,
     aDiagnostics,
     mImageContainer,
-    mLayersBackend
+    mLayersBackend,
+    mGMPCrashHelper
   });
 
   if (!mDecoder) {
     mLastError = NS_ERROR_FAILURE;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -60,15 +60,16 @@ private:
   VideoInfo mCurrentConfig;
   layers::LayersBackend mLayersBackend;
   RefPtr<layers::ImageContainer> mImageContainer;
   const RefPtr<TaskQueue> mTaskQueue;
   nsTArray<RefPtr<MediaRawData>> mMediaRawSamples;
   MediaDataDecoderCallback* mCallback;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
+  RefPtr<GMPCrashHelper> mGMPCrashHelper;
   bool mNeedAVCC;
   nsresult mLastError;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_H264Converter_h
--- a/dom/media/webaudio/BufferDecoder.cpp
+++ b/dom/media/webaudio/BufferDecoder.cpp
@@ -3,23 +3,25 @@
 /* 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 "BufferDecoder.h"
 
 #include "nsISupports.h"
 #include "MediaResource.h"
+#include "GMPService.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS0(BufferDecoder)
 
-BufferDecoder::BufferDecoder(MediaResource* aResource)
+BufferDecoder::BufferDecoder(MediaResource* aResource, GMPCrashHelper* aCrashHelper)
   : mResource(aResource)
+  , mCrashHelper(aCrashHelper)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(BufferDecoder);
 }
 
 BufferDecoder::~BufferDecoder()
 {
   // The dtor may run on any thread, we cannot be sure.
@@ -62,9 +64,15 @@ BufferDecoder::GetImageContainer()
 
 MediaDecoderOwner*
 BufferDecoder::GetOwner()
 {
   // unknown
   return nullptr;
 }
 
+already_AddRefed<GMPCrashHelper>
+BufferDecoder::GetCrashHelper()
+{
+  return do_AddRef(mCrashHelper);
+}
+
 } // namespace mozilla
--- a/dom/media/webaudio/BufferDecoder.h
+++ b/dom/media/webaudio/BufferDecoder.h
@@ -19,34 +19,37 @@ namespace mozilla {
  * This class provides a decoder object which decodes a media file that lives in
  * a memory buffer.
  */
 class BufferDecoder final : public AbstractMediaDecoder
 {
 public:
   // This class holds a weak pointer to MediaResource.  It's the responsibility
   // of the caller to manage the memory of the MediaResource object.
-  explicit BufferDecoder(MediaResource* aResource);
+  explicit BufferDecoder(MediaResource* aResource, GMPCrashHelper* aCrashHelper);
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // This has to be called before decoding begins
   void BeginDecoding(TaskQueue* aTaskQueueIdentity);
 
   MediaResource* GetResource() const final override;
 
   void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
                            uint32_t aDropped) final override;
 
   VideoFrameContainer* GetVideoFrameContainer() final override;
   layers::ImageContainer* GetImageContainer() final override;
 
   MediaDecoderOwner* GetOwner() final override;
 
+  already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
+
 private:
   virtual ~BufferDecoder();
   RefPtr<TaskQueue> mTaskQueueIdentity;
   RefPtr<MediaResource> mResource;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif /* BUFFER_DECODER_H_ */
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -20,16 +20,17 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptError.h"
 #include "nsMimeTypes.h"
 #include "VideoUtils.h"
 #include "WebAudioUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/Telemetry.h"
 #include "nsPrintfCString.h"
+#include "GMPService.h"
 
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebAudioDecodeJob)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebAudioDecodeJob)
@@ -176,16 +177,34 @@ MediaDecodeTask::Run()
     break;
   case PhaseEnum::Done:
     break;
   }
 
   return NS_OK;
 }
 
+class BufferDecoderGMPCrashHelper : public GMPCrashHelper
+{
+public:
+  explicit BufferDecoderGMPCrashHelper(nsPIDOMWindowInner* aParent)
+    : mParent(do_GetWeakReference(aParent))
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+  already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mParent);
+    return window.forget();
+  }
+private:
+  nsWeakPtr mParent;
+};
+
 bool
 MediaDecodeTask::CreateReader()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
 
   nsCOMPtr<nsIPrincipal> principal;
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mDecodeJob.mContext->GetParentObject());
@@ -193,17 +212,18 @@ MediaDecodeTask::CreateReader()
     principal = sop->GetPrincipal();
   }
 
   RefPtr<BufferMediaResource> resource =
     new BufferMediaResource(static_cast<uint8_t*> (mBuffer),
                             mLength, principal, mContentType);
 
   MOZ_ASSERT(!mBufferDecoder);
-  mBufferDecoder = new BufferDecoder(resource);
+  mBufferDecoder = new BufferDecoder(resource,
+    new BufferDecoderGMPCrashHelper(mDecodeJob.mContext->GetParentObject()));
 
   // If you change this list to add support for new decoders, please consider
   // updating HTMLMediaElement::CreateDecoder as well.
 
   mDecoderReader = DecoderTraits::CreateReader(mContentType, mBufferDecoder);
 
   if (!mDecoderReader) {
     return false;
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1177,21 +1177,28 @@ XMLHttpRequestMainThread::GetAllResponse
     aResponseHeaders.Append(value);
     if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
       aResponseHeaders.AppendLiteral(";charset=");
       aResponseHeaders.Append(value);
     }
     aResponseHeaders.AppendLiteral("\r\n");
   }
 
-  int64_t length;
-  if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
-    aResponseHeaders.AppendLiteral("Content-Length: ");
-    aResponseHeaders.AppendInt(length);
-    aResponseHeaders.AppendLiteral("\r\n");
+  // Don't provide Content-Length for data URIs
+  nsCOMPtr<nsIURI> uri;
+  bool isDataURI;
+  if (NS_FAILED(mChannel->GetURI(getter_AddRefs(uri))) ||
+      NS_FAILED(uri->SchemeIs("data", &isDataURI)) ||
+      !isDataURI) {
+    int64_t length;
+    if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
+      aResponseHeaders.AppendLiteral("Content-Length: ");
+      aResponseHeaders.AppendInt(length);
+      aResponseHeaders.AppendLiteral("\r\n");
+    }
   }
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetResponseHeader(const nsACString& aHeader,
                                             nsACString& aResult)
 {
   ErrorResult rv;
@@ -1961,16 +1968,19 @@ XMLHttpRequestMainThread::OnStartRequest
     nsCOMPtr<nsILoadGroup> loadGroup;
     channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
     rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
                                          nullptr, getter_AddRefs(listener),
                                          !isCrossSite);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // the spec requires the response document.referrer to be the empty string
+    mResponseXML->SetReferrer(NS_LITERAL_CSTRING(""));
+
     mXMLParserStreamListener = listener;
     rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // We won't get any progress events anyway if we didn't have progress
   // events when starting the request - so maybe no need to start timer here.
   if (NS_SUCCEEDED(rv) &&
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1170,16 +1170,27 @@ public:
   virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
     mPermitSubpixelAA = aPermitSubpixelAA;
   }
 
   bool GetPermitSubpixelAA() {
     return mPermitSubpixelAA;
   }
 
+  /**
+   * Ensures that no snapshot is still pointing to this DrawTarget's surface data.
+   *
+   * This can be useful if the DrawTarget is wrapped around data that it does not
+   * own, and for some reason the owner of the data has to make it temporarily
+   * unavailable without the DrawTarget knowing about it.
+   * This can cause costly surface copies, so it should not be used without a
+   * a good reason.
+   */
+  virtual void DetachAllSnapshots() = 0;
+
 #ifdef USE_SKIA_GPU
   virtual bool InitWithGrContext(GrContext* aGrContext,
                                  const IntSize &aSize,
                                  SurfaceFormat aFormat)
   {
     MOZ_CRASH("GFX: InitWithGrContext");
   }
 #endif
--- a/gfx/2d/DrawTargetCG.h
+++ b/gfx/2d/DrawTargetCG.h
@@ -123,16 +123,17 @@ public:
   friend class UnboundnessFixer;
   friend class SourceSurfaceCGBitmapContext;
   DrawTargetCG();
   virtual ~DrawTargetCG();
 
   virtual DrawTargetType GetType() const override;
   virtual BackendType GetBackendType() const override;
   virtual already_AddRefed<SourceSurface> Snapshot() override;
+  virtual void DetachAllSnapshots() override { MarkChanged(); }
 
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions = DrawSurfaceOptions(),
                            const DrawOptions &aOptions = DrawOptions()) override;
   virtual void DrawFilter(FilterNode *aNode,
                           const Rect &aSourceRect,
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -172,16 +172,18 @@ public:
   virtual void *GetNativeSurface(NativeSurfaceType aType) override;
 
   bool Init(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr);
   bool Init(const IntSize& aSize, SurfaceFormat aFormat);
   bool Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat);
 
   virtual void SetTransform(const Matrix& aTransform) override;
 
+  virtual void DetachAllSnapshots() override { MarkSnapshotIndependent(); }
+
   // Call to set up aContext for drawing (with the current transform, etc).
   // Pass the path you're going to be using if you have one.
   // Implicitly calls WillChange(aPath).
   void PrepareForDrawing(cairo_t* aContext, const Path* aPath = nullptr);
 
   static cairo_surface_t *GetDummySurface();
 
   // Cairo hardcodes this as its maximum surface size.
--- a/gfx/2d/DrawTargetCapture.cpp
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -40,16 +40,20 @@ DrawTargetCaptureImpl::Snapshot()
 {
   RefPtr<DrawTarget> dt = mRefDT->CreateSimilarDrawTarget(mSize, mRefDT->GetFormat());
 
   ReplayToDrawTarget(dt, Matrix());
 
   return dt->Snapshot();
 }
 
+void
+DrawTargetCaptureImpl::DetachAllSnapshots()
+{}
+
 #define AppendCommand(arg) new (AppendToCommandList<arg>()) arg
 
 void
 DrawTargetCaptureImpl::DrawSurface(SourceSurface *aSurface,
                                    const Rect &aDest,
                                    const Rect &aSource,
                                    const DrawSurfaceOptions &aSurfOptions,
                                    const DrawOptions &aOptions)
--- a/gfx/2d/DrawTargetCapture.h
+++ b/gfx/2d/DrawTargetCapture.h
@@ -23,16 +23,19 @@ public:
   {}
 
   bool Init(const IntSize& aSize, DrawTarget* aRefDT);
 
   virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); }
   virtual DrawTargetType GetType() const { return mRefDT->GetType(); }
 
   virtual already_AddRefed<SourceSurface> Snapshot();
+
+  virtual void DetachAllSnapshots();
+
   virtual IntSize GetSize() { return mSize; }
 
   virtual void Flush() {}
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
                            const Rect &aSource,
                            const DrawSurfaceOptions &aSurfOptions,
                            const DrawOptions &aOptions);
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -122,16 +122,18 @@ public:
 
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
 
   virtual bool SupportsRegionClipping() const override { return false; }
   virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
 
   virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
 
+  virtual void DetachAllSnapshots() override { MarkChanged(); }
+
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
   uint32_t GetByteSize() const;
 
   // This function will get an image for a surface, it may adjust the source
   // transform for any transformation of the resulting image relative to the
   // oritingal SourceSurface.
   already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
--- a/gfx/2d/DrawTargetDual.cpp
+++ b/gfx/2d/DrawTargetDual.cpp
@@ -82,16 +82,23 @@ public:
 
   const Pattern *mA;
   const Pattern *mB;
 
   bool mPatternsInitialized;
 };
 
 void
+DrawTargetDual::DetachAllSnapshots()
+{
+  mA->DetachAllSnapshots();
+  mB->DetachAllSnapshots();
+}
+
+void
 DrawTargetDual::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource,
                             const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions)
 {
   DualSurface surface(aSurface);
   mA->DrawSurface(surface.mA, aDest, aSource, aSurfOptions, aOptions);
   mB->DrawSurface(surface.mB, aDest, aSource, aSurfOptions, aOptions);
 }
 
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -44,17 +44,19 @@ public:
   }
      
   virtual DrawTargetType GetType() const override { return mA->GetType(); }
   virtual BackendType GetBackendType() const override { return mA->GetBackendType(); }
   virtual already_AddRefed<SourceSurface> Snapshot() override {
     return MakeAndAddRef<SourceSurfaceDual>(mA, mB);
   }
   virtual IntSize GetSize() override { return mA->GetSize(); }
-     
+
+  virtual void DetachAllSnapshots() override;
+
   FORWARD_FUNCTION(Flush)
   FORWARD_FUNCTION1(PushClip, const Path *, aPath)
   FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect)
   FORWARD_FUNCTION(PopClip)
   FORWARD_FUNCTION(PopLayer)
   FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
 
   virtual void SetTransform(const Matrix &aTransform) override {
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -473,16 +473,22 @@ DrawTargetRecording::Snapshot()
   RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(surf, mRecorder);
 
   mRecorder->RecordEvent(RecordedSnapshot(retSurf, this));
 
   return retSurf.forget();
 }
 
 void
+DrawTargetRecording::DetachAllSnapshots()
+{
+  mFinalDT->DetachAllSnapshots();
+}
+
+void
 DrawTargetRecording::DrawSurface(SourceSurface *aSurface,
                                  const Rect &aDest,
                                  const Rect &aSource,
                                  const DrawSurfaceOptions &aSurfOptions,
                                  const DrawOptions &aOptions)
 {
   EnsureSurfaceStored(mRecorder, aSurface, "DrawSurface");
 
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -32,16 +32,18 @@ public:
   ~DrawTargetRecording();
 
   virtual DrawTargetType GetType() const override { return mFinalDT->GetType(); }
   virtual BackendType GetBackendType() const override { return mFinalDT->GetBackendType(); }
   virtual bool IsRecording() const override { return true; }
 
   virtual already_AddRefed<SourceSurface> Snapshot() override;
 
+  virtual void DetachAllSnapshots() override;
+
   virtual IntSize GetSize() override { return mFinalDT->GetSize(); }
 
   /* Ensure that the DrawTarget backend has flushed all drawing operations to
    * this draw target. This must be called before using the backing surface of
    * this draw target outside of GFX 2D code.
    */
   virtual void Flush() override { mFinalDT->Flush(); }
 
--- a/gfx/2d/DrawTargetSkia.h
+++ b/gfx/2d/DrawTargetSkia.h
@@ -120,16 +120,17 @@ public:
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
   virtual already_AddRefed<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
   virtual already_AddRefed<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
   virtual void SetTransform(const Matrix &aTransform) override;
   virtual void *GetNativeSurface(NativeSurfaceType aType) override;
+  virtual void DetachAllSnapshots() override { MarkChanged(); }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   void Init(unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
 
 #ifdef USE_SKIA_GPU
   bool InitWithGrContext(GrContext* aGrContext,
                          const IntSize &aSize,
                          SurfaceFormat aFormat,
--- a/gfx/2d/DrawTargetTiled.cpp
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -49,16 +49,20 @@ DrawTargetTiled::Init(const TileSet& aTi
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetTiled::Snapshot()
 {
   return MakeAndAddRef<SnapshotTiled>(mTiles, mRect);
 }
 
+void
+DrawTargetTiled::DetachAllSnapshots()
+{}
+
 // Skip the mClippedOut check since this is only used for Flush() which
 // should happen even if we're clipped.
 #define TILED_COMMAND(command) \
   void \
   DrawTargetTiled::command() \
   { \
     for (size_t i = 0; i < mTiles.size(); i++) { \
       mTiles[i].mDrawTarget->command(); \
--- a/gfx/2d/DrawTargetTiled.h
+++ b/gfx/2d/DrawTargetTiled.h
@@ -36,16 +36,17 @@ public:
 
   bool Init(const TileSet& mTiles);
 
   virtual bool IsTiledDrawTarget() const override { return true; }
 
   virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); }
   virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); }
   virtual already_AddRefed<SourceSurface> Snapshot() override;
+  virtual void DetachAllSnapshots() override;
   virtual IntSize GetSize() override {
     MOZ_ASSERT(mRect.width > 0 && mRect.height > 0);
     return IntSize(mRect.XMost(), mRect.YMost());
   }
 
   virtual void Flush() override;
   virtual void DrawSurface(SourceSurface *aSurface,
                            const Rect &aDest,
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -93,16 +93,32 @@ BytesPerPixel(SurfaceFormat aFormat)
     return 3 * sizeof(float);
   case SurfaceFormat::Depth:
     return sizeof(uint16_t);
   default:
     return 4;
   }
 }
 
+static inline bool
+IsOpaqueFormat(SurfaceFormat aFormat) {
+  switch (aFormat) {
+    case SurfaceFormat::B8G8R8X8:
+    case SurfaceFormat::R8G8B8X8:
+    case SurfaceFormat::X8R8G8B8:
+    case SurfaceFormat::YUV:
+    case SurfaceFormat::NV12:
+    case SurfaceFormat::YUV422:
+    case SurfaceFormat::R5G6B5_UINT16:
+      return true;
+    default:
+      return false;
+  }
+}
+
 template<typename T, int alignment = 16>
 struct AlignedArray
 {
   typedef T value_type;
 
   AlignedArray()
     : mPtr(nullptr)
     , mStorage(nullptr)
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1111,19 +1111,20 @@ GLContext::LoadMoreSymbols(const char* p
                                                               const SymLoadStruct* extList,
                                                               GLFeature feature)
     {
         const bool useCore = this->IsFeatureProvidedByCoreSymbols(feature);
         const auto list = useCore ? coreList : extList;
         return fnLoadForFeature(list, feature);
     };
 
-    bool hasRobustness = false;
-    if (SupportsRobustness()) {
-        if (IsExtensionSupported(ARB_robustness)) {
+    if (IsSupported(GLFeature::robustness)) {
+        bool hasRobustness = false;
+
+        if (!hasRobustness && IsExtensionSupported(ARB_robustness)) {
             const SymLoadStruct symbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusARB", nullptr } },
                 END_SYMBOLS
             };
             if (fnLoadForExt(symbols, ARB_robustness)) {
                 hasRobustness = true;
             }
         }
@@ -1132,19 +1133,20 @@ GLContext::LoadMoreSymbols(const char* p
             const SymLoadStruct symbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGetGraphicsResetStatus, { "GetGraphicsResetStatusEXT", nullptr } },
                 END_SYMBOLS
             };
             if (fnLoadForExt(symbols, EXT_robustness)) {
                 hasRobustness = true;
             }
         }
-    }
-    if (!hasRobustness) {
-        MarkUnsupported(GLFeature::robustness);
+
+        if (!hasRobustness) {
+            MarkUnsupported(GLFeature::robustness);
+        }
     }
 
     if (IsSupported(GLFeature::sync)) {
         const SymLoadStruct symbols[] = {
             { (PRFuncPtr*) &mSymbols.fFenceSync,      { "FenceSync",      nullptr } },
             { (PRFuncPtr*) &mSymbols.fIsSync,         { "IsSync",         nullptr } },
             { (PRFuncPtr*) &mSymbols.fDeleteSync,     { "DeleteSync",     nullptr } },
             { (PRFuncPtr*) &mSymbols.fClientWaitSync, { "ClientWaitSync", nullptr } },
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -199,17 +199,17 @@ WGLLibrary::EnsureInitialized()
     };
 
     if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0], lookupFunc))
     {
         const char* extString = fGetExtensionsString(mWindowDC);
         MOZ_ASSERT(extString);
         MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string"));
 
-        if (HasExtension(extString, "WGL_ARB_context_create")) {
+        if (HasExtension(extString, "WGL_ARB_create_context")) {
             if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0], lookupFunc)) {
                 if (HasExtension(extString, "WGL_ARB_create_context_robustness")) {
                     mHasRobustness = true;
                 }
             } else {
                 NS_ERROR("WGL supports ARB_create_context without supplying its functions.");
                 fCreateContextAttribs = nullptr;
             }
@@ -548,18 +548,18 @@ CreatePBufferOffscreenContext(CreateCont
 
     HGLRC context;
     if (wgl.HasRobustness()) {
         int attribs[] = {
             LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
             LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
             0
         };
-
-        context = wgl.fCreateContextAttribs(pbdc, aShareContext->Context(), attribs);
+        const HGLRC shareHandle = (aShareContext ? aShareContext->Context() : 0);
+        context = wgl.fCreateContextAttribs(pbdc, shareHandle, attribs);
     } else {
         context = wgl.fCreateContext(pbdc);
         if (context && aShareContext) {
             if (!wgl.fShareLists(aShareContext->Context(), context)) {
                 wgl.fDeleteContext(context);
                 context = nullptr;
                 printf_stderr("ERROR - creating pbuffer context failed because wglShareLists returned FALSE");
             }
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -144,16 +144,22 @@ GLScreenBuffer::GLScreenBuffer(GLContext
 #endif
 { }
 
 GLScreenBuffer::~GLScreenBuffer()
 {
     mFactory = nullptr;
     mDraw = nullptr;
     mRead = nullptr;
+
+    if (!mBack)
+        return;
+
+    // Detach mBack cleanly.
+    mBack->Surf()->ProducerRelease();
 }
 
 void
 GLScreenBuffer::BindAsFramebuffer(GLContext* const gl, GLenum target) const
 {
     GLuint drawFB = DrawFB();
     GLuint readFB = ReadFB();
 
@@ -464,18 +470,20 @@ GLScreenBuffer::Morph(UniquePtr<SurfaceF
     mFactory = Move(newFactory);
 }
 
 bool
 GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& size)
 {
     ScopedBindFramebuffer autoFB(mGL);
 
-    if (mRead && SharedSurf())
+    const bool readNeedsUnlock = (mRead && SharedSurf());
+    if (readNeedsUnlock) {
         SharedSurf()->UnlockProd();
+    }
 
     surf->LockProd();
 
     if (mRead &&
         surf->mAttachType == SharedSurf()->mAttachType &&
         size == Size())
     {
         // Same size, same type, ready for reuse!
@@ -496,17 +504,19 @@ GLScreenBuffer::Attach(SharedSurface* su
         if (!mDraw || size != Size())
             drawOk = CreateDraw(size, &draw);  // Can be null.
 
         UniquePtr<ReadBuffer> read = CreateRead(surf);
         bool readOk = !!read;
 
         if (!drawOk || !readOk) {
             surf->UnlockProd();
-
+            if (readNeedsUnlock) {
+                SharedSurf()->LockProd();
+            }
             return false;
         }
 
         if (draw)
             mDraw = Move(draw);
 
         mRead = Move(read);
     }
@@ -539,18 +549,20 @@ GLScreenBuffer::Swap(const gfx::IntSize&
         return false;
 
     // In the case of DXGL interop, the new surface needs to be acquired before
     // it is attached so that the interop surface is locked, which populates
     // the GL renderbuffer. This results in the renderbuffer being ready and
     // attachment to framebuffer succeeds in Attach() call.
     newBack->Surf()->ProducerAcquire();
 
-    if (!Attach(newBack->Surf(), size))
+    if (!Attach(newBack->Surf(), size)) {
+        newBack->Surf()->ProducerRelease();
         return false;
+    }
     // Attach was successful.
 
     mFront = mBack;
     mBack = newBack;
 
     if (ShouldPreserveBuffer() &&
         mFront &&
         mBack &&
--- a/gfx/gl/SharedSurfaceD3D11Interop.cpp
+++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp
@@ -309,24 +309,16 @@ SharedSurface_D3D11Interop::~SharedSurfa
         return;
 
     mGL->fDeleteRenderbuffers(1, &mProdRB);
 
     // mDXGL is closed when it runs out of refs.
 }
 
 void
-SharedSurface_D3D11Interop::LockProdImpl()
-{ }
-
-void
-SharedSurface_D3D11Interop::UnlockProdImpl()
-{ }
-
-void
 SharedSurface_D3D11Interop::ProducerAcquireImpl()
 {
     MOZ_ASSERT(!mLockedForGL);
 
     if (mKeyedMutex) {
         const uint64_t keyValue = 0;
         const DWORD timeoutMs = 10000;
         HRESULT hr = mKeyedMutex->AcquireSync(keyValue, timeoutMs);
--- a/gfx/gl/SharedSurfaceD3D11Interop.h
+++ b/gfx/gl/SharedSurfaceD3D11Interop.h
@@ -55,18 +55,18 @@ protected:
                                HANDLE objectWGL,
                                const RefPtr<ID3D11Texture2D>& textureD3D,
                                HANDLE sharedHandle,
                                const RefPtr<IDXGIKeyedMutex>& keyedMutex);
 
 public:
     virtual ~SharedSurface_D3D11Interop();
 
-    virtual void LockProdImpl() override;
-    virtual void UnlockProdImpl() override;
+    virtual void LockProdImpl() override { }
+    virtual void UnlockProdImpl() override { }
 
     virtual void ProducerAcquireImpl() override;
     virtual void ProducerReleaseImpl() override;
 
     virtual GLuint ProdRenderbuffer() override {
         return mProdRB;
     }
 
--- a/gfx/ipc/CompositorSession.cpp
+++ b/gfx/ipc/CompositorSession.cpp
@@ -1,83 +1,93 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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 "CompositorSession.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
 #include "base/process_util.h"
 
 namespace mozilla {
 namespace layers {
 
+using namespace widget;
+
 class InProcessCompositorSession final : public CompositorSession
 {
 public:
   InProcessCompositorSession(
-    widget::CompositorWidgetProxy* aWidgetProxy,
+    nsIWidget* aWidget,
     ClientLayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   CompositorBridgeParent* GetInProcessBridge() const override;
   void SetContentController(GeckoContentController* aController) override;
   uint64_t RootLayerTreeId() const override;
   already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const override;
   void Shutdown() override;
 
 private:
   RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+  RefPtr<CompositorWidget> mCompositorWidget;
 };
 
 already_AddRefed<CompositorSession>
-CompositorSession::CreateInProcess(widget::CompositorWidgetProxy* aWidgetProxy,
+CompositorSession::CreateInProcess(nsIWidget* aWidget,
                                    ClientLayerManager* aLayerManager,
                                    CSSToLayoutDeviceScale aScale,
                                    bool aUseAPZ,
                                    bool aUseExternalSurfaceSize,
                                    const gfx::IntSize& aSurfaceSize)
 {
   RefPtr<InProcessCompositorSession> session = new InProcessCompositorSession(
-    aWidgetProxy,
+    aWidget,
     aLayerManager,
     aScale,
     aUseAPZ,
     aUseExternalSurfaceSize,
     aSurfaceSize);
   return session.forget();
 }
 
 CompositorSession::CompositorSession()
+ : mCompositorWidgetDelegate(nullptr)
 {
 }
 
 CompositorSession::~CompositorSession()
 {
 }
 
 CompositorBridgeChild*
 CompositorSession::GetCompositorBridgeChild()
 {
   return mCompositorBridgeChild;
 }
 
-InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidgetProxy* aWidgetProxy,
+InProcessCompositorSession::InProcessCompositorSession(nsIWidget* aWidget,
                                                        ClientLayerManager* aLayerManager,
                                                        CSSToLayoutDeviceScale aScale,
                                                        bool aUseAPZ,
                                                        bool aUseExternalSurfaceSize,
                                                        const gfx::IntSize& aSurfaceSize)
 {
+  CompositorWidgetInitData initData;
+  aWidget->GetCompositorWidgetInitData(&initData);
+  mCompositorWidget = CompositorWidget::CreateLocal(initData, aWidget);
+  mCompositorWidgetDelegate = mCompositorWidget->AsDelegate();
+
   mCompositorBridgeParent = new CompositorBridgeParent(
-    aWidgetProxy,
+    mCompositorWidget,
     aScale,
     aUseAPZ,
     aUseExternalSurfaceSize,
     aSurfaceSize);
   mCompositorBridgeChild = new CompositorBridgeChild(aLayerManager);
   mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent);
   mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId());
 }
@@ -111,12 +121,13 @@ InProcessCompositorSession::Shutdown()
 {
   // Destroy will synchronously wait for the parent to acknowledge shutdown,
   // at which point CBP will defer a Release on the compositor thread. We
   // can safely release our reference now, and let the destructor run on either
   // thread.
   mCompositorBridgeChild->Destroy();
   mCompositorBridgeChild = nullptr;
   mCompositorBridgeParent = nullptr;
+  mCompositorWidget = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/ipc/CompositorSession.h
+++ b/gfx/ipc/CompositorSession.h
@@ -6,19 +6,22 @@
 #ifndef _include_mozilla_gfx_ipc_CompositorSession_h_
 #define _include_mozilla_gfx_ipc_CompositorSession_h_
 
 #include "base/basictypes.h"
 #include "Units.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/gfx/Point.h"
 
+class nsIWidget;
+
 namespace mozilla {
 namespace widget {
-class CompositorWidgetProxy;
+class CompositorWidget;
+class CompositorWidgetDelegate;
 } // namespace widget
 namespace gfx {
 class GPUProcessManager;
 } // namespace gfx
 namespace layers {
 
 class GeckoContentController;
 class APZCTreeManager;
@@ -27,16 +30,20 @@ class CompositorBridgeChild;
 class ClientLayerManager;
 
 // A CompositorSession provides access to a compositor without exposing whether
 // or not it's in-process or out-of-process.
 class CompositorSession
 {
   friend class gfx::GPUProcessManager;
 
+protected:
+  typedef widget::CompositorWidget CompositorWidget;
+  typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate;
+
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession)
 
   virtual void Shutdown() = 0;
 
   // This returns a CompositorBridgeParent if the compositor resides in the same process.
   virtual CompositorBridgeParent* GetInProcessBridge() const = 0;
 
@@ -47,30 +54,36 @@ public:
   virtual uint64_t RootLayerTreeId() const = 0;
 
   // Return the Async Pan/Zoom Tree Manager for this compositor.
   virtual already_AddRefed<APZCTreeManager> GetAPZCTreeManager() const = 0;
 
   // Return the child end of the compositor IPC bridge.
   CompositorBridgeChild* GetCompositorBridgeChild();
 
+  // Return the proxy for accessing the compositor's widget.
+  CompositorWidgetDelegate* GetCompositorWidgetDelegate() {
+    return mCompositorWidgetDelegate;
+  }
+
 protected:
   CompositorSession();
   virtual ~CompositorSession();
 
   static already_AddRefed<CompositorSession> CreateInProcess(
-    widget::CompositorWidgetProxy* aWidgetProxy,
+    nsIWidget* aWidget,
     ClientLayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
 protected:
   RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
+  CompositorWidgetDelegate* mCompositorWidgetDelegate;
 
 private:
   DISALLOW_COPY_AND_ASSIGN(CompositorSession);
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -145,25 +145,25 @@ GPUProcessManager::DestroyProcess()
   }
 
   mProcess->Shutdown();
   mProcess = nullptr;
   mGPUChild = nullptr;
 }
 
 already_AddRefed<CompositorSession>
-GPUProcessManager::CreateTopLevelCompositor(widget::CompositorWidgetProxy* aProxy,
+GPUProcessManager::CreateTopLevelCompositor(nsIWidget* aWidget,
                                             ClientLayerManager* aLayerManager,
                                             CSSToLayoutDeviceScale aScale,
                                             bool aUseAPZ,
                                             bool aUseExternalSurfaceSize,
                                             const gfx::IntSize& aSurfaceSize)
 {
   return CompositorSession::CreateInProcess(
-    aProxy,
+    aWidget,
     aLayerManager,
     aScale,
     aUseAPZ,
     aUseExternalSurfaceSize,
     aSurfaceSize);
 }
 
 PCompositorBridgeParent*
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -19,17 +19,17 @@ namespace mozilla {
 namespace layers {
 class APZCTreeManager;
 class CompositorSession;
 class ClientLayerManager;
 class CompositorUpdateObserver;
 class PCompositorBridgeParent;
 } // namespace layers
 namespace widget {
-class CompositorWidgetProxy;
+class CompositorWidget;
 } // namespace widget
 namespace dom {
 class ContentParent;
 class TabParent;
 } // namespace dom
 namespace ipc {
 class GeckoChildProcessHost;
 } // namespace ipc
@@ -56,17 +56,17 @@ public:
   void EnableGPUProcess();
 
   // Ensure that GPU-bound methods can be used. If no GPU process is being
   // used, or one is launched and ready, this function returns immediately.
   // Otherwise it blocks until the GPU process has finished launching.
   void EnsureGPUReady();
 
   already_AddRefed<layers::CompositorSession> CreateTopLevelCompositor(
-    widget::CompositorWidgetProxy* aProxy,
+    nsIWidget* aWidget,
     layers::ClientLayerManager* aLayerManager,
     CSSToLayoutDeviceScale aScale,
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   layers::PCompositorBridgeParent* CreateTabCompositorBridge(
     ipc::Transport* aTransport,
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -98,18 +98,17 @@ static bool UsingX11Compositor()
 }
 
 static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
                                          LayersBackend aLayersBackend)
 {
   return aLayersBackend != LayersBackend::LAYERS_BASIC
       || UsingX11Compositor()
       || aFormat == gfx::SurfaceFormat::UNKNOWN
-      || aFormat == gfx::SurfaceFormat::YUV
-      || aFormat == gfx::SurfaceFormat::NV12;
+      || aFormat == gfx::SurfaceFormat::YUV;
 }
 
 BufferTextureData*
 BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                           gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
                           TextureAllocationFlags aAllocFlags,
                           ClientIPCAllocator* aAllocator)
 {
@@ -147,17 +146,16 @@ BufferTextureData::CreateInternal(Client
 
     return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
   }
   return nullptr;
 }
 
 BufferTextureData*
 BufferTextureData::CreateForYCbCrWithBufferSize(ClientIPCAllocator* aAllocator,
-                                                gfx::SurfaceFormat aFormat,
                                                 int32_t aBufferSize,
                                                 TextureFlags aTextureFlags)
 {
   if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
     return nullptr;
   }
 
   // Initialize the metadata with something, even if it will have to be rewritten
@@ -206,17 +204,16 @@ BufferTextureData::FillInfo(TextureData:
   if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
     aInfo.hasIntermediateBuffer = true;
   } else {
     aInfo.hasIntermediateBuffer = mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
   }
 
   switch (aInfo.format) {
     case gfx::SurfaceFormat::YUV:
-    case gfx::SurfaceFormat::NV12:
     case gfx::SurfaceFormat::UNKNOWN:
       aInfo.supportsMoz2D = false;
       break;
     default:
       aInfo.supportsMoz2D = true;
   }
 }
 
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -28,17 +28,16 @@ public:
                                            gfx::IntSize aCbCrSize,
                                            StereoMode aStereoMode,
                                            TextureFlags aTextureFlags);
 
   // It is generally better to use CreateForYCbCr instead.
   // This creates a half-initialized texture since we don't know the sizes and
   // offsets in the buffer.
   static BufferTextureData* CreateForYCbCrWithBufferSize(ClientIPCAllocator* aAllocator,
-                                                         gfx::SurfaceFormat aFormat,
                                                          int32_t aSize,
                                                          TextureFlags aTextureFlags);
 
   virtual bool Lock(OpenMode aMode, FenceHandle*) override { return true; }
 
   virtual void Unlock() override {}
 
   virtual void FillInfo(TextureData::Info& aInfo) const override;
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -20,17 +20,17 @@
 #include "nsWindow.h"
 #include "nsScreenManagerGonk.h"
 #endif
 
 namespace mozilla {
 
 namespace layers {
 
-Compositor::Compositor(widget::CompositorWidgetProxy* aWidget,
+Compositor::Compositor(widget::CompositorWidget* aWidget,
                       CompositorBridgeParent* aParent)
   : mCompositorID(0)
   , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
   , mParent(aParent)
   , mPixelsPerFrame(0)
   , mPixelsFilled(0)
   , mScreenRotation(ROTATION_0)
   , mWidget(aWidget)
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -12,21 +12,21 @@
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for IntSize, Point
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for Float
 #include "mozilla/layers/CompositorTypes.h"  // for DiagnosticTypes, etc
 #include "mozilla/layers/FenceUtils.h"  // for FenceHandle
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
+#include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"
 #include <vector>
 #include "mozilla/WidgetUtils.h"
-#include "CompositorWidgetProxy.h"
 
 /**
  * Different elements of a web pages are rendered into separate "layers" before
  * they are flattened into the final image that is brought to the screen.
  * See Layers.h for more informations about layers and why we use retained
  * structures.
  * Most of the documentation for layers is directly in the source code in the
  * form of doc comments. An overview can also be found in the the wiki:
@@ -187,17 +187,17 @@ enum SurfaceInitMode
 class Compositor
 {
 protected:
   virtual ~Compositor();
 
 public:
   NS_INLINE_DECL_REFCOUNTING(Compositor)
 
-  explicit Compositor(widget::CompositorWidgetProxy* aWidget,
+  explicit Compositor(widget::CompositorWidget* aWidget,
                       CompositorBridgeParent* aParent = nullptr);
 
   virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
 
   virtual bool Initialize() = 0;
@@ -469,17 +469,17 @@ public:
   /**
    * Call before rendering begins to ensure the compositor is ready to
    * composite. Returns false if rendering should be aborted.
    */
   virtual bool Ready() { return true; }
 
   virtual void ForcePresent() { }
 
-  widget::CompositorWidgetProxy* GetWidget() const { return mWidget; }
+  widget::CompositorWidget* GetWidget() const { return mWidget; }
 
   virtual bool HasImageHostOverlays() { return false; }
 
   virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) {}
 
   virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) {}
 
   /**
@@ -613,17 +613,17 @@ protected:
   size_t mPixelsPerFrame;
   size_t mPixelsFilled;
 
   ScreenRotation mScreenRotation;
 
   RefPtr<gfx::DrawTarget> mTarget;
   gfx::IntRect mTargetBounds;
 
-  widget::CompositorWidgetProxy* mWidget;
+  widget::CompositorWidget* mWidget;
 
   bool mIsDestroyed;
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   FenceHandle mReleaseFenceHandle;
 #endif
 
 private:
--- a/gfx/layers/CopyableCanvasLayer.cpp
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -80,30 +80,42 @@ bool
 CopyableCanvasLayer::IsDataValid(const Data& aData)
 {
   return mGLContext == aData.mGLContext;
 }
 
 void
 CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
 {
+  AutoReturnSnapshot autoReturn;
+
   if (mAsyncRenderer) {
     mSurface = mAsyncRenderer->GetSurface();
   } else if (!mGLFrontbuffer && mBufferProvider) {
-    mSurface = mBufferProvider->GetSnapshot();
+    mSurface = mBufferProvider->BorrowSnapshot();
+    if (aDestTarget) {
+      // If !aDestTarget we'll end up painting using mSurface later,
+      // so we can't return it to the provider (note that this will trigger a
+      // copy of the snapshot behind the scenes when the provider is unlocked).
+      autoReturn.mSnapshot = &mSurface;
+    }
+    // Either way we need to call ReturnSnapshot because ther may be an
+    // underlying TextureClient that has to be unlocked.
+    autoReturn.mBufferProvider = mBufferProvider;
   }
 
   if (!mGLContext && aDestTarget) {
     NS_ASSERTION(mSurface, "Must have surface to draw!");
     if (mSurface) {
       aDestTarget->CopySurface(mSurface,
                                IntRect(0, 0, mBounds.width, mBounds.height),
                                IntPoint(0, 0));
       mSurface = nullptr;
     }
+
     return;
   }
 
   if ((!mGLFrontbuffer && mBufferProvider) || mAsyncRenderer) {
     return;
   }
 
   MOZ_ASSERT(mGLContext);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -172,27 +172,22 @@ LayerManager::CreateDrawTarget(const Int
     CreateOffscreenCanvasDrawTarget(aSize, aFormat);
 }
 
 already_AddRefed<PersistentBufferProvider>
 LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize,
                                              mozilla::gfx::SurfaceFormat aFormat)
 {
   RefPtr<PersistentBufferProviderBasic> bufferProvider =
-    new PersistentBufferProviderBasic(aSize, aFormat,
-                                      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
+    PersistentBufferProviderBasic::Create(aSize, aFormat,
+      gfxPlatform::GetPlatform()->GetPreferredCanvasBackend());
 
-  if (!bufferProvider->IsValid()) {
-    bufferProvider =
-      new PersistentBufferProviderBasic(aSize, aFormat,
-                                        gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
-  }
-
-  if (!bufferProvider->IsValid()) {
-    return nullptr;
+  if (!bufferProvider) {
+    bufferProvider = PersistentBufferProviderBasic::Create(aSize, aFormat,
+      gfxPlatform::GetPlatform()->GetFallbackCanvasBackend());
   }
 
   return bufferProvider.forget();
 }
 
 #ifdef DEBUG
 void
 LayerManager::Mutated(Layer* aLayer)
--- a/gfx/layers/PersistentBufferProvider.cpp
+++ b/gfx/layers/PersistentBufferProvider.cpp
@@ -11,16 +11,284 @@
 #include "gfxPlatform.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-PersistentBufferProviderBasic::PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
-                                                             gfx::BackendType aBackend)
+PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
+: mDrawTarget(aDt)
+{
+  MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
+}
+
+PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
+{
+  MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
+{
+  MOZ_ASSERT(!mSnapshot);
+  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+  return dt.forget();
+}
+
+bool
+PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+  RefPtr<gfx::DrawTarget> dt(aDT);
+  MOZ_ASSERT(mDrawTarget == dt);
+  return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderBasic::BorrowSnapshot()
+{
+  mSnapshot = mDrawTarget->Snapshot();
+  RefPtr<SourceSurface> snapshot = mSnapshot;
+  return snapshot.forget();
+}
+
+void
+PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+  RefPtr<SourceSurface> snapshot = aSnapshot;
+  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+  mSnapshot = nullptr;
+}
+
+//static
+already_AddRefed<PersistentBufferProviderBasic>
+PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                      gfx::BackendType aBackend)
+{
+  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+
+  if (!dt) {
+    return nullptr;
+  }
+
+  RefPtr<PersistentBufferProviderBasic> provider =
+    new PersistentBufferProviderBasic(dt);
+
+  return provider.forget();
+}
+
+
+//static
+already_AddRefed<PersistentBufferProviderShared>
+PersistentBufferProviderShared::Create(gfx::IntSize aSize,
+                                       gfx::SurfaceFormat aFormat,
+                                       CompositableForwarder* aFwd)
+{
+  if (!aFwd || !aFwd->IPCOpen()) {
+    return nullptr;
+  }
+
+  RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
+    aFwd, aFormat, aSize,
+    BackendSelector::Canvas,
+    TextureFlags::DEFAULT,
+    TextureAllocationFlags::ALLOC_DEFAULT
+  );
+
+  if (!texture) {
+    return nullptr;
+  }
+
+  texture->EnableReadLock();
+
+  RefPtr<PersistentBufferProviderShared> provider =
+    new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
+  return provider.forget();
+}
+
+PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
+                                                               gfx::SurfaceFormat aFormat,
+                                                               CompositableForwarder* aFwd,
+                                                               RefPtr<TextureClient>& aTexture)
+
+: mSize(aSize)
+, mFormat(aFormat)
+, mFwd(aFwd)
+, mBack(aTexture.forget())
+{
+  MOZ_COUNT_CTOR(PersistentBufferProviderShared);
+}
+
+PersistentBufferProviderShared::~PersistentBufferProviderShared()
+{
+  MOZ_COUNT_DTOR(PersistentBufferProviderShared);
+
+  if (IsActivityTracked()) {
+    mFwd->GetActiveResourceTracker().RemoveObject(this);
+  }
+
+  Destroy();
+}
+
+already_AddRefed<gfx::DrawTarget>
+PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 {
-  mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
+  if (!mFwd->IPCOpen()) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(!mSnapshot);
+
+  if (IsActivityTracked()) {
+    mFwd->GetActiveResourceTracker().MarkUsed(this);
+  } else {
+    mFwd->GetActiveResourceTracker().AddObject(this);
+  }
+
+  if (!mDrawTarget) {
+    bool changedBackBuffer = false;
+    if (!mBack || mBack->IsReadLocked()) {
+      if (mBuffer && !mBuffer->IsReadLocked()) {
+        mBack.swap(mBuffer);
+      } else {
+        mBack = TextureClient::CreateForDrawing(
+          mFwd, mFormat, mSize,
+          BackendSelector::Canvas,
+          TextureFlags::DEFAULT,
+          TextureAllocationFlags::ALLOC_DEFAULT
+        );
+        if (mBack) {
+          mBack->EnableReadLock();
+        }
+      }
+      changedBackBuffer = true;
+    } else {
+      // Fast path, our front buffer is already writable because the texture upload
+      // has completed on the compositor side.
+      if (mBack->HasIntermediateBuffer()) {
+        // No need to keep an extra buffer around
+        mBuffer = nullptr;
+      }
+    }
+
+    if (!mBack || !mBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+      return nullptr;
+    }
+
+    if (changedBackBuffer && !aPersistedRect.IsEmpty()
+        && mFront && mFront->Lock(OpenMode::OPEN_READ)) {
+
+      DebugOnly<bool> success = mFront->CopyToTextureClient(mBack, &aPersistedRect, nullptr);
+      MOZ_ASSERT(success);
+
+      mFront->Unlock();
+    }
+
+    mDrawTarget = mBack->BorrowDrawTarget();
+    if (!mDrawTarget) {
+      return nullptr;
+    }
+  }
+
+  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
+  return dt.forget();
+}
+
+bool
+PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
+{
+  RefPtr<gfx::DrawTarget> dt(aDT);
+  MOZ_ASSERT(mDrawTarget == dt);
+  MOZ_ASSERT(!mSnapshot);
+
+  mDrawTarget = nullptr;
+  dt = nullptr;
+
+  mBack->Unlock();
+
+  if (!mBuffer && mFront && !mFront->IsLocked()) {
+    mBuffer.swap(mFront);
+  }
+
+  mFront = mBack;
+
+  return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+PersistentBufferProviderShared::BorrowSnapshot()
+{
+  // TODO[nical] currently we can't snapshot while drawing, looks like it does
+  // the job but I am not sure whether we want to be able to do that.
+  MOZ_ASSERT(!mDrawTarget);
+
+  if (!mFront || mFront->IsLocked()) {
+    MOZ_ASSERT(false);
+    return nullptr;
+  }
+
+  if (!mFront->Lock(OpenMode::OPEN_READ)) {
+    return nullptr;
+  }
+
+  mDrawTarget = mFront->BorrowDrawTarget();
+
+  if (!mDrawTarget) {
+    mFront->Unlock();
+  }
+
+  mSnapshot = mDrawTarget->Snapshot();
+
+  RefPtr<SourceSurface> snapshot = mSnapshot;
+  return snapshot.forget();
+}
+
+void
+PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
+{
+  RefPtr<SourceSurface> snapshot = aSnapshot;
+  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
+
+  mSnapshot = nullptr;
+  snapshot = nullptr;
+
+  mDrawTarget = nullptr;
+
+  mFront->Unlock();
+}
+
+void
+PersistentBufferProviderShared::NotifyInactive()
+{
+  if (mBuffer && mBuffer->IsLocked()) {
+    // mBuffer should never be locked
+    MOZ_ASSERT(false);
+    mBuffer->Unlock();
+  }
+  mBuffer = nullptr;
+}
+
+void
+PersistentBufferProviderShared::Destroy()
+{
+  mSnapshot = nullptr;
+  mDrawTarget = nullptr;
+
+  if (mFront && mFront->IsLocked()) {
+    mFront->Unlock();
+  }
+
+  if (mBack && mBack->IsLocked()) {
+    mBack->Unlock();
+  }
+
+  if (mBuffer && mBuffer->IsLocked()) {
+    mBuffer->Unlock();
+  }
+
+  mFront = nullptr;
+  mBack = nullptr;
+  mBuffer = nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
\ No newline at end of file
--- a/gfx/layers/PersistentBufferProvider.h
+++ b/gfx/layers/PersistentBufferProvider.h
@@ -8,16 +8,22 @@
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed, etc
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/gfx/Types.h"
 
 namespace mozilla {
+
+namespace gfx {
+  class SourceSurface;
+  class DrawTarget;
+}
+
 namespace layers {
 
 class CopyableCanvasLayer;
 
 /**
  * A PersistentBufferProvider is for users which require the temporary use of
  * a DrawTarget to draw into. When they're done drawing they return the
  * DrawTarget, when they later need to continue drawing they get a DrawTarget
@@ -33,51 +39,133 @@ public:
 
   virtual LayersBackend GetType() { return LayersBackend::LAYERS_NONE; }
 
   /**
    * Get a DrawTarget from the PersistentBufferProvider.
    *
    * \param aPersistedRect This indicates the area of the DrawTarget that needs
    *                       to have remained the same since the call to
-   *                       ReturnAndUseDT.
+   *                       ReturnDrawTarget.
    */
-  virtual already_AddRefed<gfx::DrawTarget> GetDT(const gfx::IntRect& aPersistedRect) = 0;
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) = 0;
+
   /**
    * Return a DrawTarget to the PersistentBufferProvider and indicate the
    * contents of this DrawTarget is to be considered current by the
    * BufferProvider. The caller should forget any references to the DrawTarget.
    */
-  virtual bool ReturnAndUseDT(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) = 0;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() = 0;
+
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) = 0;
 
-  virtual already_AddRefed<gfx::SourceSurface> GetSnapshot() = 0;
-protected:
+  virtual TextureClient* GetTextureClient() { return nullptr; }
+
+  virtual void OnShutdown() {}
 };
 
+
 class PersistentBufferProviderBasic : public PersistentBufferProvider
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic)
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic, override)
+
+  static already_AddRefed<PersistentBufferProviderBasic>
+  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aBackend);
+
+  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget);
+
+  virtual LayersBackend GetType() override { return LayersBackend::LAYERS_BASIC; }
+
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
+
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+private:
+  ~PersistentBufferProviderBasic();
 
-  PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
-                                gfx::BackendType aBackend);
-  explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget) : mDrawTarget(aTarget) {}
+  RefPtr<gfx::DrawTarget> mDrawTarget;
+  RefPtr<gfx::SourceSurface> mSnapshot;
+};
+
+
+/**
+ * Provides access to a buffer which can be sent to the compositor without
+ * requiring a copy.
+ */
+class PersistentBufferProviderShared : public PersistentBufferProvider
+                                     , public ActiveResource
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderShared, override)
+
+  static already_AddRefed<PersistentBufferProviderShared>
+  Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+         CompositableForwarder* aFwd);
+
+  virtual LayersBackend GetType() override { return LayersBackend::LAYERS_CLIENT; }
+
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(const gfx::IntRect& aPersistedRect) override;
+
+  virtual bool ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) override;
+
+  virtual already_AddRefed<gfx::SourceSurface> BorrowSnapshot() override;
 
-  bool IsValid() { return !!mDrawTarget; }
-  virtual LayersBackend GetType() { return LayersBackend::LAYERS_BASIC; }
-  already_AddRefed<gfx::DrawTarget> GetDT(const gfx::IntRect& aPersistedRect) {
-    RefPtr<gfx::DrawTarget> dt(mDrawTarget);
-    return dt.forget();
+  virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
+
+  TextureClient* GetTextureClient() override {
+    return mFront;
   }
-  bool ReturnAndUseDT(already_AddRefed<gfx::DrawTarget> aDT) {
-    RefPtr<gfx::DrawTarget> dt(aDT);
-    MOZ_ASSERT(mDrawTarget == dt);
-    return true;
+
+  virtual void NotifyInactive() override;
+
+  virtual void OnShutdown() override { Destroy(); }
+
+protected:
+  PersistentBufferProviderShared(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                 CompositableForwarder* aFwd,
+                                 RefPtr<TextureClient>& aTexture);
+
+  ~PersistentBufferProviderShared();
+
+  void Destroy();
+
+  gfx::IntSize mSize;
+  gfx::SurfaceFormat mFormat;
+  RefPtr<CompositableForwarder> mFwd;
+  // The texture presented to the compositor.
+  RefPtr<TextureClient> mFront;
+  // The texture that the canvas uses.
+  RefPtr<TextureClient> mBack;
+  // An extra texture we keep around temporarily to avoid allocating.
+  RefPtr<TextureClient> mBuffer;
+  RefPtr<gfx::DrawTarget> mDrawTarget;
+  RefPtr<gfx::SourceSurface > mSnapshot;
+};
+
+struct AutoReturnSnapshot
+{
+  PersistentBufferProvider* mBufferProvider;
+  RefPtr<gfx::SourceSurface>* mSnapshot;
+
+  explicit AutoReturnSnapshot(PersistentBufferProvider* aProvider = nullptr)
+  : mBufferProvider(aProvider)
+  , mSnapshot(nullptr)
+  {}
+
+  ~AutoReturnSnapshot()
+  {
+    if (mBufferProvider) {
+      mBufferProvider->ReturnSnapshot(mSnapshot ? mSnapshot->forget() : nullptr);
+    }
   }
-  virtual already_AddRefed<gfx::SourceSurface> GetSnapshot() { return mDrawTarget->Snapshot(); }
-private:
-  RefPtr<gfx::DrawTarget> mDrawTarget;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -75,17 +75,17 @@ public:
     SetUpdateSerial(0);
   }
 
 public:
   RefPtr<gfx::DataSourceSurface> mSurface;
   bool mWrappingExistingData;
 };
 
-BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget)
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
   : Compositor(aWidget, aParent)
   , mDidExternalComposition(false)
 {
   MOZ_COUNT_CTOR(BasicCompositor);
 
   mMaxTextureSize =
     Factory::GetMaxSurfaceSize(gfxPlatform::GetPlatform()->GetContentBackendFor(LayersBackend::LAYERS_BASIC));
 }
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -36,17 +36,17 @@ public:
 
   RefPtr<gfx::DrawTarget> mDrawTarget;
   gfx::IntSize mSize;
 };
 
 class BasicCompositor : public Compositor
 {
 public:
-  explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy *aWidget);
+  explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
 
 protected:
   virtual ~BasicCompositor();
 
 public:
 
   virtual BasicCompositor* AsBasicCompositor() override { return this; }
 
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -41,17 +41,17 @@ public:
 private:
   // We are going to buffer layer content on this xlib draw target
   RefPtr<mozilla::gfx::DrawTarget> mBufferDrawTarget;
 };
 
 class X11BasicCompositor : public BasicCompositor
 {
 public:
-  explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget)
+  explicit X11BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
     : BasicCompositor(aParent, aWidget)
   {}
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -62,16 +62,40 @@ CanvasClientBridge::UpdateAsync(AsyncCan
   }
 
   static_cast<ShadowLayerForwarder*>(GetForwarder())
     ->AttachAsyncCompositable(asyncID, mLayer);
   mAsyncID = asyncID;
 }
 
 void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+  MOZ_ASSERT(aTexture);
+
+  if (!aTexture->IsSharedWithCompositor()) {
+    if (!AddTextureClient(aTexture)) {
+      return;
+    }
+  }
+
+  mBackBuffer = aTexture;
+
+  AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+  CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+  t->mTextureClient = mBackBuffer;
+  t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+  t->mFrameID = mFrameID;
+  t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID();
+
+  GetForwarder()->UseTextures(this, textures);
+  aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   AutoRemoveTexture autoRemove(this);
   if (mBackBuffer &&
       (mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) {
     autoRemove.mTexture = mBackBuffer;
     mBackBuffer = nullptr;
   }
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
+#include "mozilla/layers/PersistentBufferProvider.h"
 
 // Fix X11 header brain damage that conflicts with MaybeOneOf::None
 #undef None
 #include "mozilla/MaybeOneOf.h"
 
 #include "mozilla/mozalloc.h"           // for operator delete
 
 #include "mozilla/gfx/Point.h"          // for IntSize
@@ -71,16 +72,18 @@ public:
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
     ++mFrameID;
     return CompositableClient::AddTextureClient(aTexture);
   }
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
 
+  virtual void UpdateFromTexture(TextureClient* aTexture) {}
+
   virtual void Updated() { }
 
 protected:
   int32_t mFrameID;
 };
 
 // Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
 class CanvasClient2D : public CanvasClient
@@ -99,19 +102,20 @@ public:
 
   virtual void Clear() override
   {
     mBackBuffer = mFrontBuffer = nullptr;
   }
 
   virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
 
+  virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
-    MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
     return CanvasClient::AddTextureClient(aTexture);
   }
 
   virtual void OnDetach() override
   {
     mBackBuffer = mFrontBuffer = nullptr;
   }
 
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -125,17 +125,21 @@ ClientCanvasLayer::RenderLayer()
   }
 
   if (!IsDirty()) {
     return;
   }
   Painted();
 
   FirePreTransactionCallback();
-  mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+  if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+    mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+  } else {
+    mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+  }
 
   FireDidTransactionCallback();
 
   ClientManager()->Hold(this);
   mCanvasClient->Updated();
 }
 
 CanvasClient::CanvasClientType
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -843,16 +843,25 @@ ClientLayerManager::RemoveDidCompositeOb
 }
 
 bool
 ClientLayerManager::DependsOnStaleDevice() const
 {
   return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
 }
 
+
+already_AddRefed<PersistentBufferProvider>
+ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
+                                                   gfx::SurfaceFormat aFormat)
+{
+  return PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+}
+
+
 ClientLayer::~ClientLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
   MOZ_COUNT_DTOR(ClientLayer);
 }
 
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -241,16 +241,19 @@ public:
   class DidCompositeObserver {
   public:
     virtual void DidComposite() = 0;
   };
 
   void AddDidCompositeObserver(DidCompositeObserver* aObserver);
   void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
 
+  virtual already_AddRefed<PersistentBufferProvider>
+  CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
 private:
   // Listen memory-pressure event for ClientLayerManager
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -359,16 +359,18 @@ DeallocateTextureClient(TextureDeallocPa
 }
 
 void TextureClient::Destroy(bool aForceSync)
 {
   if (mActor) {
     mActor->Lock();
   }
 
+  mReadLock = nullptr;
+
   CancelWaitFenceHandleOnImageBridge();
   RefPtr<TextureChild> actor = mActor;
   mActor = nullptr;
 
   if (actor && !actor->mDestroyed.compareExchange(false, true)) {
     actor->Unlock();
     actor = nullptr;
   }
@@ -489,27 +491,32 @@ TextureClient::Unlock()
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
   if (!mIsLocked) {
     return;
   }
 
   if (mBorrowedDrawTarget) {
-    MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
     if (mOpenMode & OpenMode::OPEN_WRITE) {
       mBorrowedDrawTarget->Flush();
       if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
         // Fallback implementation for reading back, because mData does not
         // have a backend-specific implementation and returned false.
         RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
         RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
         mReadbackSink->ProcessReadback(dataSurf);
       }
     }
+
+    mBorrowedDrawTarget->DetachAllSnapshots();
+    // If this assertion is hit, it means something is holding a strong reference
+    // to our DrawTarget externally, which is not allowed.
+    MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
+
     mBorrowedDrawTarget = nullptr;
   }
 
   if (mOpenMode & OpenMode::OPEN_WRITE) {
     mUpdated = true;
   }
 
   mData->Unlock();
@@ -1114,28 +1121,27 @@ TextureClient::CreateForYCbCr(ClientIPCA
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
 }
 
 // static
 already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCrWithBufferSize(ClientIPCAllocator* aAllocator,
-                                            gfx::SurfaceFormat aFormat,
                                             size_t aSize,
                                             TextureFlags aTextureFlags)
 {
   // also test the validity of aAllocator
   MOZ_ASSERT(aAllocator && aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   TextureData* data =
-    BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aFormat, aSize,
+    BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize,
                                                     aTextureFlags);
   if (!data) {
     return nullptr;
   }
 
   return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
 }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -375,17 +375,16 @@ public:
                            TextureFlags aTextureFlags,
                            TextureAllocationFlags flags = ALLOC_DEFAULT);
 
   // Creates and allocates a TextureClient (can beaccessed through raw
   // pointers) with a certain buffer size. It's unfortunate that we need this.
   // providing format and sizes could let us do more optimization.
   static already_AddRefed<TextureClient>
   CreateForYCbCrWithBufferSize(ClientIPCAllocator* aAllocator,
-                               gfx::SurfaceFormat aFormat,
                                size_t aSize,
                                TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient of the same type.
   already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
 
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -635,16 +635,66 @@ BufferTextureHost::EnsureWrappingTexture
   }
 
   mFirstSource->SetUpdateSerial(mUpdateSerial);
   mFirstSource->SetOwner(this);
 
   return true;
 }
 
+static
+bool IsCompatibleTextureSource(TextureSource* aTexture,
+                               const BufferDescriptor& aDescriptor,
+                               Compositor* aCompositor)
+{
+  if (!aCompositor) {
+    return false;
+  }
+
+  switch (aDescriptor.type()) {
+    case BufferDescriptor::TYCbCrDescriptor: {
+      const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+      if (!aCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+        return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
+            && aTexture->GetSize() == ycbcr.ySize();
+      }
+
+      if (aTexture->GetFormat() != gfx::SurfaceFormat::A8
+          || aTexture->GetSize() != ycbcr.ySize()) {
+        return false;
+      }
+
+      auto cbTexture = aTexture->GetSubSource(1);
+      if (!cbTexture
+          || cbTexture->GetFormat() != gfx::SurfaceFormat::A8
+          || cbTexture->GetSize() != ycbcr.cbCrSize()) {
+        return false;
+      }
+
+      auto crTexture = aTexture->GetSubSource(2);
+      if (!crTexture
+          || crTexture->GetFormat() != gfx::SurfaceFormat::A8
+          || crTexture->GetSize() != ycbcr.cbCrSize()) {
+        return false;
+      }
+
+      return true;
+    }
+    case BufferDescriptor::TRGBDescriptor: {
+      const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
+      return aTexture->GetFormat() == rgb.format()
+          && aTexture->GetSize() == rgb.size();
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
 void
 BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
 {
   if (!mHasIntermediateBuffer) {
     EnsureWrappingTextureSource();
   }
 
   if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
@@ -653,32 +703,24 @@ BufferTextureHost::PrepareTextureSource(
     aTexture = mFirstSource.get();
     return;
   }
 
   // We don't own it, apparently.
   mFirstSource = nullptr;
 
   DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
-  bool compatibleFormats = texture
-                         && (mFormat == texture->GetFormat()
-                             || (mFormat == gfx::SurfaceFormat::YUV
-                                 && mCompositor
-                                 && mCompositor->SupportsEffect(EffectTypes::YCBCR)
-                                 && texture->GetNextSibling()
-                                 && texture->GetNextSibling()->GetNextSibling())
-                             || (mFormat == gfx::SurfaceFormat::YUV
-                                 && mCompositor
-                                 && !mCompositor->SupportsEffect(EffectTypes::YCBCR)
-                                 && texture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8));
+
+  bool compatibleFormats = texture && IsCompatibleTextureSource(texture,
+                                                                mDescriptor,
+                                                                mCompositor);
 
   bool shouldCreateTexture = !compatibleFormats
                            || texture->NumCompositableRefs() > 1
-                           || texture->HasOwner()
-                           || texture->GetSize() != mSize;
+                           || texture->HasOwner();
 
   if (!shouldCreateTexture) {
     mFirstSource = texture;
     mFirstSource->SetOwner(this);
     mNeedsFullUpdate = true;
 
     // It's possible that texture belonged to a different compositor,
     // so make sure we update it (and all of its siblings) to the
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -17,17 +17,17 @@
 #include "mozilla/layers/Effects.h"
 #include "nsWindowsHelpers.h"
 #include "gfxPrefs.h"
 #include "gfxConfig.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxVR.h"
 #include "mozilla/gfx/StackArray.h"
 #include "mozilla/Services.h"
-#include "mozilla/widget/WinCompositorWidgetProxy.h"
+#include "mozilla/widget/WinCompositorWidget.h"
 
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/Telemetry.h"
 #include "BlendShaderConstants.h"
 
 #include <dxgi1_2.h>
 
 namespace mozilla {
@@ -155,17 +155,17 @@ private:
     return true;
   }
 
   // Only used during initialization.
   RefPtr<ID3D11Device> mDevice;
   bool mInitOkay;
 };
 
-CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget)
+CompositorD3D11::CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
   : Compositor(aWidget, aParent)
   , mAttachments(nullptr)
   , mHwnd(nullptr)
   , mDisableSequenceForNextFrame(false)
   , mVerifyBuffersFailed(false)
 {
 }
 
@@ -211,17 +211,17 @@ CompositorD3D11::Initialize()
 
   if (!mContext) {
     gfxCriticalNote << "[D3D11] failed to get immediate context";
     return false;
   }
 
   mFeatureLevel = mDevice->GetFeatureLevel();
 
-  mHwnd = mWidget->AsWindowsProxy()->GetHwnd();
+  mHwnd = mWidget->AsWindows()->GetHwnd();
 
   memset(&mVSConstants, 0, sizeof(VertexShaderConstants));
 
   int referenceCount = 0;
   UINT size = sizeof(referenceCount);
   // If this isn't there yet it'll fail, count will remain 0, which is correct.
   mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
   referenceCount++;
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -37,17 +37,17 @@ struct PixelShaderConstants
   int blendConfig[4];
 };
 
 struct DeviceAttachmentsD3D11;
 
 class CompositorD3D11 : public Compositor
 {
 public:
-  CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget);
+  CompositorD3D11(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
   ~CompositorD3D11();
 
   virtual CompositorD3D11* AsCompositorD3D11() override { return this; }
 
   virtual bool Initialize() override;
 
   virtual TextureFactoryIdentifier
     GetTextureFactoryIdentifier() override;
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -392,16 +392,17 @@ D3D11TextureData::Create(IntSize aSize, 
                               aFlags & ALLOC_CLEAR_BUFFER,
                               aFlags & ALLOC_CLEAR_BUFFER_WHITE,
                               aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT);
 }
 
 void
 D3D11TextureData::Deallocate(ClientIPCAllocator* aAllocator)
 {
+  mDrawTarget = nullptr;
   mTexture = nullptr;
 }
 
 already_AddRefed<TextureClient>
 CreateD3D11TextureClientWithDevice(IntSize aSize, SurfaceFormat aFormat,
                                    TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
                                    ID3D11Device* aDevice,
                                    ClientIPCAllocator* aAllocator)
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -12,24 +12,24 @@
 #include "mozilla/layers/Effects.h"
 #include "nsWindowsHelpers.h"
 #include "Nv3DVUtils.h"
 #include "gfxFailure.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "gfxPrefs.h"
 #include "gfxCrashReporterUtils.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
-#include "mozilla/widget/WinCompositorWidgetProxy.h"
+#include "mozilla/widget/WinCompositorWidget.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
-CompositorD3D9::CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget)
+CompositorD3D9::CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
   : Compositor(aWidget, aParent)
   , mDeviceResetCount(0)
   , mFailedResetAttempts(0)
 {
 }
 
 CompositorD3D9::~CompositorD3D9()
 {
@@ -42,17 +42,17 @@ CompositorD3D9::Initialize()
 {
   ScopedGfxFeatureReporter reporter("D3D9 Layers");
 
   mDeviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
   if (!mDeviceManager) {
     return false;
   }
 
-  mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindowsProxy()->GetHwnd());
+  mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
   if (!mSwapChain) {
     return false;
   }
 
   if (!mWidget->InitCompositor(this)) {
     return false;
   }
 
@@ -585,17 +585,17 @@ CompositorD3D9::SetMask(const EffectChai
  */
 
 bool
 CompositorD3D9::EnsureSwapChain()
 {
   MOZ_ASSERT(mDeviceManager, "Don't call EnsureSwapChain without a device manager");
 
   if (!mSwapChain) {
-    mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindowsProxy()->GetHwnd());
+    mSwapChain = mDeviceManager->CreateSwapChain(mWidget->AsWindows()->GetHwnd());
     // We could not create a swap chain, return false
     if (!mSwapChain) {
       // Check the state of the device too
       DeviceManagerState state = mDeviceManager->VerifyReadyForRendering();
       if (state == DeviceMustRecreate) {
         mDeviceManager = nullptr;
       }
       mParent->InvalidateRemoteLayers();
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -16,17 +16,17 @@
 class nsWidget;
 
 namespace mozilla {
 namespace layers {
 
 class CompositorD3D9 : public Compositor
 {
 public:
-  CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidgetProxy* aWidget);
+  CompositorD3D9(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget);
   ~CompositorD3D9();
 
   virtual CompositorD3D9* AsCompositorD3D9() override { return this; }
 
   virtual bool Initialize() override;
 
   virtual TextureFactoryIdentifier
     GetTextureFactoryIdentifier() override;
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -13,47 +13,79 @@
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/layers/TextureForwarder.h"  // for TextureForwarder
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
+#include "nsExpirationTracker.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class AsyncTransactionTracker;
 class ImageContainer;
 struct TextureFactoryIdentifier;
 class SurfaceDescriptor;
 class SurfaceDescriptorTiles;
 class ThebesBufferData;
 class PTextureChild;
 
 /**
+ * See ActiveResourceTracker below.
+ */
+class ActiveResource
+{
+public:
+ virtual void NotifyInactive() = 0;
+  nsExpirationState* GetExpirationState() { return &mExpirationState; }
+  bool IsActivityTracked() { return mExpirationState.IsTracked(); }
+private:
+  nsExpirationState mExpirationState;
+};
+
+/**
+ * A convenience class on top of nsExpirationTracker
+ */
+class ActiveResourceTracker : public nsExpirationTracker<ActiveResource, 3>
+{
+public:
+  ActiveResourceTracker(uint32_t aExpirationCycle, const char* aName)
+  : nsExpirationTracker(aExpirationCycle, aName)
+  {}
+
+  virtual void NotifyExpired(ActiveResource* aResource) override
+  {
+    RemoveObject(aResource);
+    aResource->NotifyInactive();
+  }
+};
+
+/**
  * A transaction is a set of changes that happenned on the content side, that
  * should be sent to the compositor side.
  * CompositableForwarder is an interface to manage a transaction of
  * compositable objetcs.
  *
  * ShadowLayerForwarder is an example of a CompositableForwarder (that can
  * additionally forward modifications of the Layer tree).
  * ImageBridgeChild is another CompositableForwarder.
  */
 class CompositableForwarder : public TextureForwarder
 {
 public:
 
   CompositableForwarder(const char* aName)
     : TextureForwarder(aName)
+    , mActiveResourceTracker(1000, "CompositableForwarder")
     , mSerial(++sSerialCounter)
   {}
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
    */
   virtual void Connect(CompositableClient* aCompositable,
@@ -164,23 +196,27 @@ public:
     return mTextureFactoryIdentifier.mSupportsPartialUploads;
   }
 
   const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
   {
     return mTextureFactoryIdentifier;
   }
 
+  ActiveResourceTracker& GetActiveResourceTracker() { return mActiveResourceTracker; }
+
 protected:
   TextureFactoryIdentifier mTextureFactoryIdentifier;
 
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
   RefPtr<SyncObject> mSyncObject;
 
+  ActiveResourceTracker mActiveResourceTracker;
+
   const int32_t mSerial;
   static mozilla::Atomic<int32_t> sSerialCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -24,16 +24,20 @@
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop, etc
 #include "FrameLayerBuilder.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/unused.h"
 #include "mozilla/DebugOnly.h"
 #if defined(XP_WIN)
 #include "WinUtils.h"
 #endif
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetChild.h"
+#endif
 
 using mozilla::layers::LayerTransactionChild;
 using mozilla::dom::TabChildBase;
 using mozilla::Unused;
 
 namespace mozilla {
 namespace layers {
 
@@ -108,41 +112,40 @@ CompositorBridgeChild::Destroy()
   AutoTArray<PLayerTransactionChild*, 16> transactions;
   ManagedPLayerTransactionChild(transactions);
   for (int i = transactions.Length() - 1; i >= 0; --i) {
     RefPtr<LayerTransactionChild> layers =
       static_cast<LayerTransactionChild*>(transactions[i]);
     layers->Destroy();
   }
 
+  const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
+  for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
+    RefPtr<TextureClient> texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
+
+    if (texture) {
+      texture->Destroy();
+    }
+  }
+
   SendWillClose();
   mCanSend = false;
 
 
   // The call just made to SendWillClose can result in IPC from the
   // CompositorBridgeParent to the CompositorBridgeChild (e.g. caused by the destruction
   // of shared memory). We need to ensure this gets processed by the
   // CompositorBridgeChild before it gets destroyed. It suffices to ensure that
   // events already in the MessageLoop get processed before the
   // CompositorBridgeChild is destroyed, so we add a task to the MessageLoop to
   // handle compositor desctruction.
 
   // From now on we can't send any message message.
   MessageLoop::current()->PostTask(
              NewRunnableFunction(DeferredDestroyCompositor, mCompositorBridgeParent, selfRef));
-
-  const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
-  for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
-    RefPtr<TextureClient> texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
-
-    if (texture) {
-      texture->Destroy();
-    }
-  }
-
 }
 
 // static
 void
 CompositorBridgeChild::ShutDown()
 {
   if (sCompositorBridge) {
     sCompositorBridge->Destroy();
@@ -948,11 +951,36 @@ CompositorBridgeChild::AllocShmem(size_t
 }
 
 void
 CompositorBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
 {
     PCompositorBridgeChild::DeallocShmem(aShmem);
 }
 
+widget::PCompositorWidgetChild*
+CompositorBridgeChild::AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData)
+{
+  // We send the constructor manually.
+  MOZ_CRASH("Should not be called");
+  return nullptr;
+}
+
+bool
+CompositorBridgeChild::DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor)
+{
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+  delete aActor;
+  return true;
+#else
+  return false;
+#endif
+}
+
+void
+CompositorBridgeChild::ProcessingError(Result aCode, const char* aReason)
+{
+  MOZ_CRASH("Processing error in CompositorBridgeChild");
+}
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -188,18 +188,23 @@ public:
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                                 mozilla::ipc::Shmem* aShmem) override;
   virtual bool AllocShmem(size_t aSize,
                           mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                           mozilla::ipc::Shmem* aShmem) override;
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
+  PCompositorWidgetChild* AllocPCompositorWidgetChild(const CompositorWidgetInitData& aInitData) override;
+  bool DeallocPCompositorWidgetChild(PCompositorWidgetChild* aActor) override;
+
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
+  void ProcessingError(Result aCode, const char* aReason) override;
+
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
   virtual PLayerTransactionChild*
     AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
                                 const uint64_t& aId,
                                 TextureFactoryIdentifier* aTextureFactoryIdentifier,
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -71,16 +71,20 @@
 #include "mozilla/unused.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalTypes.h"
 #include "mozilla/StaticPtr.h"
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "ProfilerMarkers.h"
 #endif
 #include "mozilla/VsyncDispatcher.h"
+#include "mozilla/widget/CompositorWidget.h"
+#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING
+# include "mozilla/widget/CompositorWidgetParent.h"
+#endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "GeckoTouchDispatcher.h"
 #include "nsScreenManagerGonk.h"
 #endif
 
 #ifdef MOZ_ANDROID_APZ
 #include "AndroidBridge.h"
@@ -228,23 +232,23 @@ CompositorVsyncScheduler::Observer::Noti
 void
 CompositorVsyncScheduler::Observer::Destroy()
 {
   MutexAutoLock lock(mMutex);
   mOwner = nullptr;
 }
 
 CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
-                                                   widget::CompositorWidgetProxy* aWidgetProxy)
+                                                   widget::CompositorWidget* aWidget)
   : mCompositorBridgeParent(aCompositorBridgeParent)
   , mLastCompose(TimeStamp::Now())
   , mIsObservingVsync(false)
   , mNeedsComposite(0)
   , mVsyncNotificationsSkipped(0)
-  , mCompositorVsyncDispatcher(aWidgetProxy->GetCompositorVsyncDispatcher())
+  , mCompositorVsyncDispatcher(aWidget->GetCompositorVsyncDispatcher())
   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
   , mCurrentCompositeTask(nullptr)
   , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
   , mSetNeedsCompositeTask(nullptr)
 #ifdef MOZ_WIDGET_GONK
 #if ANDROID_VERSION >= 19
   , mDisplayEnabled(false)
   , mSetDisplayMonitor("SetDisplayMonitor")
@@ -577,23 +581,23 @@ CompositorVsyncScheduler::ComposeToTarge
 }
 
 static inline MessageLoop*
 CompositorLoop()
 {
   return CompositorThreadHolder::Loop();
 }
 
-CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget,
+CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidget* aWidget,
                                                CSSToLayoutDeviceScale aScale,
                                                bool aUseAPZ,
                                                bool aUseExternalSurfaceSize,
                                                const gfx::IntSize& aSurfaceSize)
   : CompositorBridgeParentIPCAllocator("CompositorBridgeParent")
-  , mWidgetProxy(aWidget)
+  , mWidget(aWidget)
   , mIsTesting(false)
   , mPendingTransaction(0)
   , mPaused(false)
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mEGLSurfaceSize(aSurfaceSize)
   , mPauseCompositionMonitor("PauseCompositionMonitor")
   , mResumeCompositionMonitor("ResumeCompositionMonitor")
   , mResetCompositorMonitor("ResetCompositorMonitor")
@@ -694,17 +698,17 @@ CompositorBridgeParent::StopAndClearReso
 
   if (mCompositor) {
     mCompositor->DetachWidget();
     mCompositor->Destroy();
     mCompositor = nullptr;
   }
 
   // After this point, it is no longer legal to access the widget.
-  mWidgetProxy = nullptr;
+  mWidget = nullptr;
 }
 
 bool
 CompositorBridgeParent::RecvWillClose()
 {
   StopAndClearResources();
   return true;
 }
@@ -1144,17 +1148,17 @@ CompositorBridgeParent::CompositeToTarge
                                &hasRemoteContent,
                                &updatePluginsFlag);
 
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   // We do not support plugins in local content. When switching tabs
   // to local pages, hide every plugin associated with the window.
   if (!hasRemoteContent && BrowserTabsRemoteAutostart() &&
       mCachedPluginData.Length()) {
-    Unused << SendHideAllPlugins(GetWidgetProxy()->GetWidgetKey());
+    Unused << SendHideAllPlugins(GetWidget()->GetWidgetKey());
     mCachedPluginData.Clear();
   }
 #endif
 
   if (aTarget) {
     mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
   } else {
     mLayerManager->BeginTransaction();
@@ -1490,34 +1494,34 @@ CompositorBridgeParent::InitializeLayerM
 
 RefPtr<Compositor>
 CompositorBridgeParent::NewCompositor(const nsTArray<LayersBackend>& aBackendHints)
 {
   for (size_t i = 0; i < aBackendHints.Length(); ++i) {
     RefPtr<Compositor> compositor;
     if (aBackendHints[i] == LayersBackend::LAYERS_OPENGL) {
       compositor = new CompositorOGL(this,
-                                     mWidgetProxy,
+                                     mWidget,
                                      mEGLSurfaceSize.width,
                                      mEGLSurfaceSize.height,
                                      mUseExternalSurfaceSize);
     } else if (aBackendHints[i] == LayersBackend::LAYERS_BASIC) {
 #ifdef MOZ_WIDGET_GTK
       if (gfxPlatformGtk::GetPlatform()->UseXRender()) {
-        compositor = new X11BasicCompositor(this, mWidgetProxy);
+        compositor = new X11BasicCompositor(this, mWidget);
       } else
 #endif
       {
-        compositor = new BasicCompositor(this, mWidgetProxy);
+        compositor = new BasicCompositor(this, mWidget);
       }
 #ifdef XP_WIN
     } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D11) {
-      compositor = new CompositorD3D11(this, mWidgetProxy);
+      compositor = new CompositorD3D11(this, mWidget);
     } else if (aBackendHints[i] == LayersBackend::LAYERS_D3D9) {
-      compositor = new CompositorD3D9(this, mWidgetProxy);
+      compositor = new CompositorD3D9(this, mWidget);
 #endif
     }
 
     if (compositor && compositor->Initialize()) {
       compositor->SetCompositorID(mCompositorID);
       return compositor;
     }
   }
@@ -1798,16 +1802,48 @@ CompositorBridgeParent::RequestNotifyLay
 /* static */ void
 CompositorBridgeParent::RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
 {
   EnsureLayerTreeMapReady();
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   sIndirectLayerTrees[aLayersId].mLayerTreeClearedObserver = aObserver;
 }
 
+widget::PCompositorWidgetParent*
+CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+  if (mWidget) {
+    // Should not create two widgets on the same compositor.
+    return nullptr;
+  }
+
+  widget::CompositorWidgetParent* widget =
+    new widget::CompositorWidgetParent(aInitData);
+  widget->AddRef();
+
+  // Sending the constructor acts as initialization as well.
+  mWidget = widget;
+  return widget;
+#else
+  return nullptr;
+#endif
+}
+
+bool
+CompositorBridgeParent::DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor)
+{
+#if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
+  static_cast<widget::CompositorWidgetParent*>(aActor)->Release();
+  return true;
+#else
+  return false;
+#endif
+}
+
 /**
  * This class handles layer updates pushed directly from child processes to
  * the compositor thread. It's associated with a CompositorBridgeParent on the
  * compositor thread. While it uses the PCompositorBridge protocol to manage
  * these updates, it doesn't actually drive compositing itself. For that it
  * hands off work to the CompositorBridgeParent it's associated with.
  */
 class CrossProcessCompositorBridgeParent final : public PCompositorBridgeParent,
@@ -1977,16 +2013,25 @@ public:
     return OtherPid();
   }
 
   virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override
   {
     Unused << SendParentAsyncMessages(aMessage);
   }
 
+  PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override {
+    // Not allowed.
+    return nullptr;
+  }
+  bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override {
+    // Not allowed.
+    return false;
+  }
+
   virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() override { return this; }
 
 protected:
   void OnChannelConnected(int32_t pid) override {
     mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
   }
 private:
   // Private destructor, to discourage deletion outside of Release():
@@ -2460,17 +2505,17 @@ CompositorBridgeParent::UpdatePluginWind
 
   if (!lts.mPluginData.Length()) {
     // Don't hide plugins if the previous remote layer tree didn't contain any.
     if (!mCachedPluginData.Length()) {
       PLUGINS_LOG("[%" PRIu64 "] nothing to hide", aId);
       return false;
     }
 
-    uintptr_t parentWidget = GetWidgetProxy()->GetWidgetKey();
+    uintptr_t parentWidget = GetWidget()->GetWidgetKey();
 
     // We will pass through here in cases where the previous shadow layer
     // tree contained visible plugins and the new tree does not. All we need
     // to do here is hide the plugins for the old tree, so don't waste time
     // calculating clipping.
     mPluginsLayerOffset = nsIntPoint(0,0);
     mPluginsLayerVisibleRegion.SetEmpty();
     Unused << lts.mParent->SendHideAllPlugins(parentWidget);
@@ -2545,17 +2590,17 @@ CompositorBridgeParent::HideAllPluginWin
 {
   MOZ_ASSERT(!NS_IsMainThread());
   // No plugins in the cache implies no plugins to manage
   // in this content.
   if (!mCachedPluginData.Length() || mDeferPluginWindows) {
     return;
   }
 
-  uintptr_t parentWidget = GetWidgetProxy()->GetWidgetKey();
+  uintptr_t parentWidget = GetWidget()->GetWidgetKey();
 
   mDeferPluginWindows = true;
   mPluginWindowsHidden = true;
   Unused << SendHideAllPlugins(parentWidget);
   ScheduleComposition();
 }
 #endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -28,20 +28,20 @@
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/SharedMemory.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator
 #include "mozilla/layers/LayersMessages.h"  // for TargetConfig
 #include "mozilla/layers/PCompositorBridgeParent.h"
 #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
 #include "mozilla/layers/APZTestData.h"
+#include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 #include "mozilla/VsyncDispatcher.h"
-#include "CompositorWidgetProxy.h"
 
 class MessageLoop;
 class nsIWidget;
 
 namespace mozilla {
 
 class CancelableRunnable;
 
@@ -85,17 +85,17 @@ private:
  * Turns vsync notifications into scheduled composites.
  **/
 class CompositorVsyncScheduler
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
 
 public:
   explicit CompositorVsyncScheduler(CompositorBridgeParent* aCompositorBridgeParent,
-                                    widget::CompositorWidgetProxy* aWidgetProxy);
+                                    widget::CompositorWidget* aWidget);
 
 #ifdef MOZ_WIDGET_GONK
   // emulator-ics never trigger the display on/off, so compositor will always
   // skip composition request at that device. Only check the display status
   // with kk device and upon.
 #if ANDROID_VERSION >= 19
   // SetDisplay() and CancelSetDisplayTask() are used for the display on/off.
   // It will clear all composition related task and flag, and skip another
@@ -207,17 +207,17 @@ class CompositorBridgeParent final : pub
                                      public ShmemAllocator
 {
   friend class CompositorVsyncScheduler;
   friend class CompositorThreadHolder;
   friend class InProcessCompositorSession;
   friend class gfx::GPUProcessManager;
 
 public:
-  explicit CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget,
+  explicit CompositorBridgeParent(widget::CompositorWidget* aWidget,
                                   CSSToLayoutDeviceScale aScale,
                                   bool aUseAPZ,
                                   bool aUseExternalSurfaceSize,
                                   const gfx::IntSize& aSurfaceSize);
 
   virtual bool RecvGetFrameUniformity(FrameUniformityData* aOutData) override;
   virtual bool RecvRequestOverfill() override;
   virtual bool RecvWillClose() override;
@@ -300,16 +300,19 @@ public:
                           mozilla::ipc::Shmem* aShmem) override;
 
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aType,
                                 mozilla::ipc::Shmem* aShmem) override;
 
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
+  PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
+  bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
+
   virtual base::ProcessId GetChildProcessId() override
   {
     return OtherPid();
   }
 
   virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
 
   virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() override { return this; }
@@ -459,17 +462,17 @@ public:
 
   /**
    * Used by the profiler to denote when a vsync occured
    */
   static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
 
   float ComputeRenderIntegrity();
 
-  widget::CompositorWidgetProxy* GetWidgetProxy() { return mWidgetProxy; }
+  widget::CompositorWidget* GetWidget() { return mWidget; }
 
   void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
 
   bool AsyncPanZoomEnabled() const {
     return !!mApzcTreeManager;
   }
 
 private:
@@ -581,17 +584,17 @@ protected:
   // The indirect layer tree lock must be held before calling this function.
   // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
   template <typename Lambda>
   inline void ForEachIndirectLayerTree(const Lambda& aCallback);
 
   RefPtr<LayerManagerComposite> mLayerManager;
   RefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
-  widget::CompositorWidgetProxy* mWidgetProxy;
+  widget::CompositorWidget* mWidget;
   TimeStamp mTestTime;
   bool mIsTesting;
 
   uint64_t mPendingTransaction;
 
   bool mPaused;
 
   bool mUseExternalSurfaceSize;
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -2,16 +2,17 @@
  * 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 "ISurfaceAllocator.h"
 
+#include "gfxPrefs.h"
 #include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
 #include "mozilla/layers/TextureHost.h"       // for TextureHost
 
 namespace mozilla {
 namespace layers {
 
 NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
 
@@ -282,10 +283,16 @@ FixedSizeSmallShmemSectionAllocator::Shr
       }
       mUsedShmems.pop_back();
     } else {
       i++;
     }
   }
 }
 
+int32_t
+ClientIPCAllocator::GetMaxTextureSize() const
+{
+  return gfxPrefs::MaxTextureSize();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -11,17 +11,16 @@
 #include "gfxTypes.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/RefPtr.h"
 #include "nsIMemoryReporter.h"          // for nsIMemoryReporter
 #include "mozilla/Atomics.h"            // for Atomic
 #include "mozilla/layers/LayersMessages.h" // for ShmemSection
 #include "LayersTypes.h"
-#include "gfxPrefs.h"
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 
 /*
  * FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
  * (I think that this doesn't apply anymore.)
  *
  * This #define is actually needed here, because subclasses of ISurfaceAllocator,
  * namely ShadowLayerForwarder, will or will not override AllocGrallocBuffer
@@ -126,17 +125,17 @@ public:
   ClientIPCAllocator(const char* aName) : ISurfaceAllocator(aName) {}
 
   virtual ClientIPCAllocator* AsClientAllocator() override { return this; }
 
   virtual base::ProcessId GetParentPid() const = 0;
 
   virtual MessageLoop * GetMessageLoop() const = 0;
 
-  virtual int32_t GetMaxTextureSize() const { return gfxPrefs::MaxTextureSize(); }
+  virtual int32_t GetMaxTextureSize() const;
 
   virtual void CancelWaitForRecycle(uint64_t aTextureId) = 0;
 };
 
 /// Methods that are specific to the host/parent side.
 class HostIPCAllocator : public ISurfaceAllocator
 {
 public:
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -2,18 +2,20 @@
  * 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 PlatformWidgetTypes;
 include protocol PBrowser;
 include protocol PCompositable;
+include protocol PCompositorWidget;
 include protocol PImageContainer;
 include protocol PLayer;
 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";
@@ -42,16 +44,17 @@ namespace layers {
  * the main thread and the compositor thread context. It's primary
  * purpose is to manage the PLayerTransaction sub protocol.
  */
 sync protocol PCompositorBridge
 {
   // A Compositor manages a single Layer Manager (PLayerTransaction)
   manages PLayerTransaction;
   manages PTexture;
+  manages PCompositorWidget;
 
 child:
   // The child should invalidate retained layers. This is used for local
   // compositor device resets, such as in CompositorD3D9, and ensures that
   // TextureSources are recreated.
   async InvalidateLayers(uint64_t layersId);
 
   // The compositor type or device has changed, and a new texture factory
@@ -98,16 +101,18 @@ child:
    * Drop any buffers that might be retained on the child compositor
    * side.
    */
   async ClearCachedResources(uint64_t id);
 
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
 parent:
+  async PCompositorWidget(CompositorWidgetInitData aInitData);
+
   /**
    * Confirmation callback for UpdatePluginConfigurations and HideAllPlugins.
    */
   async RemotePluginsReady();
 
   // Confirmation that the child has invalidated all its layers, and will not
   // request layers against an old compositor.
   async AcknowledgeCompositorUpdate(uint64_t id);
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -114,17 +114,17 @@ SharedPlanarYCbCrImage::AllocateAndGetNe
 {
   MOZ_ASSERT(!mTextureClient, "This image already has allocated data");
   size_t size = ImageDataSerializer::ComputeYCbCrBufferSize(aSize);
   if (!size) {
     return nullptr;
   }
 
   mTextureClient = TextureClient::CreateForYCbCrWithBufferSize(mCompositable->GetForwarder(),
-                                                               gfx::SurfaceFormat::YUV, size,
+                                                               size,
                                                                mCompositable->GetTextureFlags());
 
   // get new buffer _without_ setting mBuffer.
   if (!mTextureClient) {
     return nullptr;
   }
 
   // update buffer size
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -79,17 +79,17 @@ CompositorOGL::BindBackdrop(ShaderProgra
   mGLContext->fActiveTexture(aTexUnit);
   mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
   mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
   mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
   aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
 }
 
 CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent,
-                             widget::CompositorWidgetProxy* aWidget,
+                             widget::CompositorWidget* aWidget,
                              int aSurfaceWidth, int aSurfaceHeight,
                              bool aUseExternalSurfaceSize)
   : Compositor(aWidget, aParent)
   , mWidgetSize(-1, -1)
   , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mHasBGRA(0)
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mFrameInProgress(false)
@@ -613,17 +613,17 @@ CompositingRenderTarget*
 CompositorOGL::GetCurrentRenderTarget() const
 {
   return mCurrentRenderTarget;
 }
 
 static GLenum
 GetFrameBufferInternalFormat(GLContext* gl,
                              GLuint aFrameBuffer,
-                             mozilla::widget::CompositorWidgetProxy* aWidget)
+                             mozilla::widget::CompositorWidget* aWidget)
 {
   if (aFrameBuffer == 0) { // default framebuffer
     return aWidget->GetGLFrameBufferFormat();
   }
   return LOCAL_GL_RGBA;
 }
 
 void
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -188,17 +188,17 @@ class CompositorOGL final : public Compo
   typedef mozilla::gl::GLContext GLContext;
 
   friend class GLManagerCompositor;
   friend class CompositingRenderTargetOGL;
 
   std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms;
 public:
   explicit CompositorOGL(CompositorBridgeParent* aParent,
-                         widget::CompositorWidgetProxy* aWidget,
+                         widget::CompositorWidget* aWidget,
                          int aSurfaceWidth = -1, int aSurfaceHeight = -1,
                          bool aUseExternalSurfaceSize = false);
 
 protected:
   virtual ~CompositorOGL();
 
 public:
   virtual CompositorOGL* AsCompositorOGL() override { return this; }
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -10,16 +10,17 @@
 #include "TestLayers.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/widget/InProcessCompositorWidget.h"
 #include "nsBaseWidget.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include <vector>
 
 const int gCompWidth = 256;
 const int gCompHeight = 256;
 
@@ -94,31 +95,31 @@ private:
   ~MockWidget() {}
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
 
 struct LayerManagerData {
   RefPtr<MockWidget> mWidget;
   RefPtr<Compositor> mCompositor;
-  RefPtr<widget::CompositorWidgetProxy> mCompositorWidgetProxy;
+  RefPtr<widget::CompositorWidget> mCompositorWidget;
   RefPtr<LayerManagerComposite> mLayerManager;
 
   LayerManagerData(Compositor* compositor,
                    MockWidget* widget,
-                   widget::CompositorWidgetProxy* aProxy,
+                   widget::CompositorWidget* aWidget,
                    LayerManagerComposite* layerManager)
     : mWidget(widget)
     , mCompositor(compositor)
-    , mCompositorWidgetProxy(aProxy)
+    , mCompositorWidget(aWidget)
     , mLayerManager(layerManager)
   {}
 };
 
-static already_AddRefed<Compositor> CreateTestCompositor(LayersBackend backend, widget::CompositorWidgetProxy* widget)
+static already_AddRefed<Compositor> CreateTestCompositor(LayersBackend backend, widget::CompositorWidget* widget)
 {
   gfxPrefs::GetSingleton();
 
   RefPtr<Compositor> compositor;
 
   if (backend == LayersBackend::LAYERS_OPENGL) {
     compositor = new CompositorOGL(nullptr,
                                    widget,
@@ -152,17 +153,17 @@ static already_AddRefed<Compositor> Crea
 static std::vector<LayerManagerData> GetLayerManagers(std::vector<LayersBackend> aBackends)
 {
   std::vector<LayerManagerData> managers;
 
   for (size_t i = 0; i < aBackends.size(); i++) {
     auto backend = aBackends[i];
 
     RefPtr<MockWidget> widget = new MockWidget();
-    RefPtr<widget::CompositorWidgetProxy> proxy = widget->NewCompositorWidgetProxy();
+    RefPtr<widget::CompositorWidget> proxy = new widget::InProcessCompositorWidget(widget);
     RefPtr<Compositor> compositor = CreateTestCompositor(backend, proxy);
 
     RefPtr<LayerManagerComposite> layerManager = new LayerManagerComposite(compositor);
 
     managers.push_back(LayerManagerData(compositor, widget, proxy, layerManager));
   }
 
   return managers;
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -726,22 +726,31 @@ gfxMacPlatformFontList::InitSingleFaceLi
 {
     AutoTArray<nsString, 10> singleFaceFonts;
     gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
 
     uint32_t numFonts = singleFaceFonts.Length();
     for (uint32_t i = 0; i < numFonts; i++) {
         LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
                       NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
-        gfxFontEntry *fontEntry = LookupLocalFont(singleFaceFonts[i],
+        nsAutoString familyName(singleFaceFonts[i]);
+        auto colon = familyName.FindChar(':');
+        if (colon != kNotFound) {
+            nsAutoString key(Substring(familyName, colon + 1));
+            ToLowerCase(key);
+            if (!mFontFamilies.GetWeak(key)) {
+                continue;
+            }
+            familyName.Truncate(colon);
+        }
+        gfxFontEntry *fontEntry = LookupLocalFont(familyName,
                                                   400, 0,
                                                   NS_FONT_STYLE_NORMAL);
         if (fontEntry) {
-            nsAutoString familyName, key;
-            familyName = singleFaceFonts[i];
+            nsAutoString key;
             GenerateFontListKey(familyName, key);
             LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
                           NS_ConvertUTF16toUTF8(familyName).get(),
                           NS_ConvertUTF16toUTF8(key).get()));
 
             // add only if doesn't exist already
             if (!mFontFamilies.GetWeak(key)) {
                 gfxFontFamily *familyEntry =
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -532,16 +532,17 @@ private:
   DECL_GFX_PREF(Live, "webgl.all-angle-options",               WebGLAllANGLEOptions, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
   DECL_GFX_PREF(Live, "webgl.angle.force-warp",                WebGLANGLEForceWARP, bool, false);
   DECL_GFX_PREF(Live, "webgl.bypass-shader-validation",        WebGLBypassShaderValidator, bool, true);
   DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground",  WebGLCanLoseContextInForeground, bool, true);
   DECL_GFX_PREF(Live, "webgl.default-no-alpha",                WebGLDefaultNoAlpha, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-angle",                   WebGLDisableANGLE, bool, false);
+  DECL_GFX_PREF(Live, "webgl.disable-wgl",                     WebGLDisableWGL, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-extensions",              WebGLDisableExtensions, bool, false);
   DECL_GFX_PREF(Live, "webgl.dxgl.enabled",                    WebGLDXGLEnabled, bool, false);
   DECL_GFX_PREF(Live, "webgl.dxgl.needs-finish",               WebGLDXGLNeedsFinish, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
                 WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-DOM-blit-uploads",
                 WebGLDisableDOMBlitUploads, bool, false);
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -7874,16 +7874,20 @@ TryInstantiate(JSContext* cx, CallArgs a
     }
 
     Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
     for (const AsmJSImport& import : metadata.asmJSImports) {
         if (!funcImports.append(ffis[import.ffiIndex()]))
             return false;
     }
 
+    instanceObj.set(WasmInstanceObject::create(cx));
+    if (!instanceObj)
+        return false;
+
     if (!module.instantiate(cx, funcImports, heap, instanceObj))
         return false;
 
     // Now write the imported values into global data.
     uint8_t* globalData = instanceObj->instance().codeSegment().globalData();
     uint32_t valIndex = 0;
     for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
         if (global.which() == AsmJSGlobal::Variable)
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -2686,17 +2686,17 @@ class BaseCompiler
             masm.storeLoadFence();
 #else
         MOZ_CRASH("BaseCompiler platform hook: memoryBarrier");
 #endif
     }
 
     // Cloned from MIRGraph.cpp, merge somehow?
 
-    bool needsBoundsCheckBranch(const MAsmJSHeapAccess& access) const {
+    bool needsBoundsCheckBranch(const MWasmMemoryAccess& access) const {
         // A heap access needs a bounds-check branch if we're not relying on signal
         // handlers to catch errors, and if it's not proven to be within bounds.
         // We use signal-handlers on x64, but on x86 there isn't enough address
         // space for a guard region.  Also, on x64 the atomic loads and stores
         // can't (yet) use the signal handlers.
 
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
         if (mg_.args.useSignalHandlersForOOB && !access.isAtomicAccess())
@@ -2714,17 +2714,17 @@ class BaseCompiler
 #ifdef DEBUG
         // TODO / MISSING: this needs to be adapted from what's in the
         // platform's CodeGenerator; that code takes an LAllocation as
         // the last arg now.
 #endif
     }
 #endif
 
-    void loadHeap(const MAsmJSHeapAccess& access, RegI32 ptr, AnyReg dest) {
+    void loadHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg dest) {
 #if defined(JS_CODEGEN_X64)
         // CodeGeneratorX64::visitAsmJSLoadHeap()
 
         if (needsBoundsCheckBranch(access))
             MOZ_CRASH("BaseCompiler platform hook: bounds checking");
 
         Operand srcAddr(HeapReg, ptr.reg, TimesOne, access.offset());
 
@@ -2745,17 +2745,17 @@ class BaseCompiler
 
         masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
         verifyHeapAccessDisassembly(before, after, IsLoad(true), access.accessType(), 0, srcAddr, dest);
 #else
         MOZ_CRASH("BaseCompiler platform hook: loadHeap");
 #endif
     }
 
-    void storeHeap(const MAsmJSHeapAccess& access, RegI32 ptr, AnyReg src) {
+    void storeHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg src) {
 #if defined(JS_CODEGEN_X64)
         // CodeGeneratorX64::visitAsmJSStoreHeap()
 
         if (needsBoundsCheckBranch(access))
             MOZ_CRASH("BaseCompiler platform hook: bounds checking");
 
         Operand dstAddr(HeapReg, ptr.reg, TimesOne, access.offset());
 
@@ -5021,19 +5021,18 @@ BaseCompiler::emitLoad(ValType type, Sca
 {
     LinearMemoryAddress<Nothing> addr;
     if (!iter_.readLoad(type, Scalar::byteSize(viewType), &addr))
         return false;
 
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
-    MAsmJSHeapAccess access(viewType);
+    MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
-    access.setAlign(addr.align);
 
     switch (type) {
       case ValType::I32: {
         RegI32 rp = popI32();
         loadHeap(access, rp, AnyReg(rp));
         pushI32(rp);
         break;
       }
@@ -5069,19 +5068,18 @@ BaseCompiler::emitStore(ValType resultTy
     LinearMemoryAddress<Nothing> addr;
     Nothing unused_value;
     if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, &unused_value))
         return false;
 
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
-    MAsmJSHeapAccess access(viewType);
+    MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
-    access.setAlign(addr.align);
 
     switch (resultType) {
       case ValType::I32: {
         RegI32 rp, rv;
         pop2xI32(&rp, &rv);
         storeHeap(access, rp, AnyReg(rv));
         freeI32(rp);
         pushI32(rv);
@@ -5344,19 +5342,18 @@ BaseCompiler::emitStoreWithCoercion(ValT
     LinearMemoryAddress<Nothing> addr;
     Nothing unused_value;
     if (!iter_.readStore(resultType, Scalar::byteSize(viewType), &addr, &unused_value))
         return false;
 
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
-    MAsmJSHeapAccess access(viewType);
+    MWasmMemoryAccess access(viewType, addr.align);
     access.setOffset(addr.offset);
-    access.setAlign(addr.align);
 
     if (resultType == ValType::F32 && viewType == Scalar::Float64) {
         RegF32 rv = popF32();
         RegF64 rw = needF64();
         masm.convertFloat32ToDouble(rv.reg, rw.reg);
         RegI32 rp = popI32();
         storeHeap(access, rp, AnyReg(rw));
         pushF32(rv);
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -465,24 +465,19 @@ static const char ExportField[] = "expor
 Instance::create(JSContext* cx,
                  UniqueCodeSegment codeSegment,
                  const Metadata& metadata,
                  const ShareableBytes* maybeBytecode,
                  TypedFuncTableVector&& typedFuncTables,
                  HandleArrayBufferObjectMaybeShared heap,
                  Handle<FunctionVector> funcImports,
                  const ExportMap& exportMap,
-                 MutableHandleWasmInstanceObject instanceObj)
+                 HandleWasmInstanceObject instanceObj)
 {
-    // Ensure that the Instance is traceable via WasmInstanceObject before any
-    // GC can occur.
-
-    instanceObj.set(WasmInstanceObject::create(cx));
-    if (!instanceObj)
-        return false;
+    // Ensure that the Instance is traceable via 'instanceObj' before any GC.
 
     {
         auto instance = cx->make_unique<Instance>(Move(codeSegment), metadata, maybeBytecode,
                                                   Move(typedFuncTables), heap);
         if (!instance)
             return false;
 
         instanceObj->init(Move(instance));
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -95,17 +95,17 @@ class Instance
     static bool create(JSContext* cx,
                        UniqueCodeSegment codeSegment,
                        const Metadata& metadata,
                        const ShareableBytes* maybeBytecode,
                        TypedFuncTableVector&& typedFuncTables,
                        HandleArrayBufferObjectMaybeShared heap,
                        Handle<FunctionVector> funcImports,
                        const ExportMap& exports,
-                       MutableHandle<WasmInstanceObject*> instanceObj);
+                       HandleWasmInstanceObject instanceObj);
     ~Instance();
     void trace(JSTracer* trc);
 
     const CodeSegment& codeSegment() const { return *codeSegment_; }
     const Metadata& metadata() const { return *metadata_; }
     SharedMem<uint8_t*> heap() const;
     size_t heapLength() const;
 
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -584,124 +584,105 @@ class FunctionCompiler
 
     void assign(unsigned slot, MDefinition* def)
     {
         if (inDeadCode())
             return;
         curBlock_->setSlot(info().localSlot(slot), def);
     }
 
-    MDefinition* loadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
+  private:
+    MDefinition* loadHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access)
     {
         if (inDeadCode())
             return nullptr;
-
-        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD loads should use loadSimdHeap");
-        MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
-        curBlock_->add(load);
-        return load;
-    }
-
-    MDefinition* loadSimdHeap(MDefinition* base, const MAsmJSHeapAccess& access)
-    {
-        if (inDeadCode())
-            return nullptr;
-
-        MOZ_ASSERT(Scalar::isSimdType(access.accessType()),
-                   "loadSimdHeap can only load from a SIMD view");
         MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
         curBlock_->add(load);
         return load;
     }
 
-    void storeHeap(MDefinition* base, const MAsmJSHeapAccess& access, MDefinition* v)
+    void storeHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
     {
         if (inDeadCode())
             return;
-
-        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()),
-                   "SIMD stores should use storeSimdHeap");
-        MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
-        curBlock_->add(store);
-    }
-
-    void storeSimdHeap(MDefinition* base, const MAsmJSHeapAccess& access, MDefinition* v)
-    {
-        if (inDeadCode())
-            return;
-
-        MOZ_ASSERT(Scalar::isSimdType(access.accessType()),
-                   "storeSimdHeap can only load from a SIMD view");
         MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
         curBlock_->add(store);
     }
 
-    MDefinition* atomicLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
+  public:
+    MDefinition* loadHeap(MDefinition* base, const MWasmMemoryAccess& access)
     {
-        if (inDeadCode())
-            return nullptr;
-
-        MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
-        curBlock_->add(load);
-        return load;
+        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD loads should use loadSimdHeap");
+        return loadHeapPrivate(base, access);
+    }
+    MDefinition* loadSimdHeap(MDefinition* base, const MWasmMemoryAccess& access)
+    {
+        MOZ_ASSERT(Scalar::isSimdType(access.accessType()), "non-SIMD loads should use loadHeap");
+        return loadHeapPrivate(base, access);
+    }
+    MDefinition* loadAtomicHeap(MDefinition* base, const MWasmMemoryAccess& access)
+    {
+        return loadHeapPrivate(base, access);
     }
 
-    void atomicStoreHeap(MDefinition* base, const MAsmJSHeapAccess& access,
-                         MDefinition* v)
+    void storeHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
+    {
+        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD store should use storeSimdHeap");
+        storeHeapPrivate(base, access, v);
+    }
+    void storeSimdHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
     {
-        if (inDeadCode())
-            return;
-
-        MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
-        curBlock_->add(store);
+        MOZ_ASSERT(Scalar::isSimdType(access.accessType()), "non-SIMD stores should use storeHeap");
+        storeHeapPrivate(base, access, v);
     }
-
-    MDefinition* atomicCompareExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
+    void storeAtomicHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
+    {
+        storeHeapPrivate(base, access, v);
+    }
+
+    MDefinition* atomicCompareExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
                                            MDefinition* oldv, MDefinition* newv)
     {
         if (inDeadCode())
             return nullptr;
 
-        MAsmJSCompareExchangeHeap* cas =
-            MAsmJSCompareExchangeHeap::New(alloc(), base, access, oldv, newv);
+        auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), base, access, oldv, newv);
         curBlock_->add(cas);
         return cas;
     }
 
-    MDefinition* atomicExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
+    MDefinition* atomicExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
                                     MDefinition* value)
     {
         if (inDeadCode())
             return nullptr;
 
-        MAsmJSAtomicExchangeHeap* cas =
-            MAsmJSAtomicExchangeHeap::New(alloc(), base, access, value);
+        auto* cas = MAsmJSAtomicExchangeHeap::New(alloc(), base, access, value);
         curBlock_->add(cas);
         return cas;
     }
 
     MDefinition* atomicBinopHeap(js::jit::AtomicOp op,
-                                 MDefinition* base, const MAsmJSHeapAccess& access,
+                                 MDefinition* base, const MWasmMemoryAccess& access,
                                  MDefinition* v)
     {
         if (inDeadCode())
             return nullptr;
 
-        MAsmJSAtomicBinopHeap* binop =
-            MAsmJSAtomicBinopHeap::New(alloc(), op, base, access, v);
+        auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, base, access, v);
         curBlock_->add(binop);
         return binop;
     }
 
     MDefinition* loadGlobalVar(unsigned globalDataOffset, bool isConst, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
-        MAsmJSLoadGlobalVar* load = MAsmJSLoadGlobalVar::New(alloc(), type, globalDataOffset,
-                                                             isConst);
+
+        auto* load = MAsmJSLoadGlobalVar::New(alloc(), type, globalDataOffset, isConst);
         curBlock_->add(load);
         return load;
     }
 
     void storeGlobalVar(uint32_t globalDataOffset, MDefinition* v)
     {
         if (inDeadCode())
             return;
@@ -2075,18 +2056,18 @@ EmitSelect(FunctionCompiler& f)
 }
 
 enum class IsAtomic {
     No = false,
     Yes = true
 };
 
 static bool
-SetHeapAccessOffset(FunctionCompiler& f, uint32_t offset, MAsmJSHeapAccess* access, MDefinition** base,
-                    IsAtomic atomic = IsAtomic::No)
+SetHeapAccessOffset(FunctionCompiler& f, uint32_t offset, MWasmMemoryAccess* access,
+                    MDefinition** base, IsAtomic atomic = IsAtomic::No)
 {
     // TODO Remove this after implementing non-wraparound offset semantics.
     uint32_t endOffset = offset + access->byteSize();
     if (endOffset < offset)
         return false;
 
     // Assume worst case.
     if (endOffset > f.mirGen().foldableOffsetRange(/* bounds check */ true, bool(atomic))) {
@@ -2102,18 +2083,17 @@ SetHeapAccessOffset(FunctionCompiler& f,
 
 static bool
 EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType)
 {
     LinearMemoryAddress<MDefinition*> addr;
     if (!f.iter().readLoad(type, Scalar::byteSize(viewType), &addr))
         return false;
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
         return false;
 
     f.iter().setResult(f.loadHeap(base, access));
     return true;
 }
@@ -2121,18 +2101,17 @@ EmitLoad(FunctionCompiler& f, ValType ty
 static bool
 EmitStore(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
 {
     LinearMemoryAddress<MDefinition*> addr;
     MDefinition* value;
     if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
         return false;
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
         return false;
 
     f.storeHeap(base, access, value);
     return true;
 }
@@ -2147,18 +2126,17 @@ EmitStoreWithCoercion(FunctionCompiler& 
 
     if (resultType == ValType::F32 && viewType == Scalar::Float64)
         value = f.unary<MToDouble>(value);
     else if (resultType == ValType::F64 && viewType == Scalar::Float32)
         value = f.unary<MToFloat32>(value);
     else
         MOZ_CRASH("unexpected coerced store");
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
         return false;
 
     f.storeHeap(base, access, value);
     return true;
 }
@@ -2224,60 +2202,57 @@ EmitBinaryMathBuiltinCall(FunctionCompil
 static bool
 EmitAtomicsLoad(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     if (!f.iter().readAtomicLoad(&addr, &viewType))
         return false;
 
-    MAsmJSHeapAccess access(viewType, 0, MembarBeforeLoad, MembarAfterLoad);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align, 0, MembarBeforeLoad, MembarAfterLoad);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
         return false;
 
-    f.iter().setResult(f.atomicLoadHeap(base, access));
+    f.iter().setResult(f.loadAtomicHeap(base, access));
     return true;
 }
 
 static bool
 EmitAtomicsStore(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     MDefinition* value;
     if (!f.iter().readAtomicStore(&addr, &viewType, &value))
         return false;
 
-    MAsmJSHeapAccess access(viewType, 0, MembarBeforeStore, MembarAfterStore);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align, 0, MembarBeforeStore, MembarAfterStore);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
         return false;
 
-    f.atomicStoreHeap(base, access, value);
+    f.storeAtomicHeap(base, access, value);
     f.iter().setResult(value);
     return true;
 }
 
 static bool
 EmitAtomicsBinOp(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     jit::AtomicOp op;
     MDefinition* value;
     if (!f.iter().readAtomicBinOp(&addr, &viewType, &op, &value))
         return false;
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
         return false;
 
     f.iter().setResult(f.atomicBinopHeap(op, base, access, value));
     return true;
 }
@@ -2287,18 +2262,17 @@ EmitAtomicsCompareExchange(FunctionCompi
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     MDefinition* oldValue;
     MDefinition* newValue;
     if (!f.iter().readAtomicCompareExchange(&addr, &viewType, &oldValue, &newValue))
         return false;
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
         return false;
 
     f.iter().setResult(f.atomicCompareExchangeHeap(base, access, oldValue, newValue));
     return true;
 }
@@ -2307,18 +2281,17 @@ static bool
 EmitAtomicsExchange(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     MDefinition* value;
     if (!f.iter().readAtomicExchange(&addr, &viewType, &value))
         return false;
 
-    MAsmJSHeapAccess access(viewType);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base, IsAtomic::Yes))
         return false;
 
     f.iter().setResult(f.atomicExchangeHeap(base, access, value));
     return true;
 }
@@ -2535,18 +2508,17 @@ EmitSimdLoad(FunctionCompiler& f, ValTyp
 
     if (!numElems)
         numElems = defaultNumElems;
 
     LinearMemoryAddress<MDefinition*> addr;
     if (!f.iter().readLoad(resultType, Scalar::byteSize(viewType), &addr))
         return false;
 
-    MAsmJSHeapAccess access(viewType, numElems);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align, numElems);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
         return false;
 
     f.iter().setResult(f.loadSimdHeap(base, access));
     return true;
 }
@@ -2560,18 +2532,17 @@ EmitSimdStore(FunctionCompiler& f, ValTy
     if (!numElems)
         numElems = defaultNumElems;
 
     LinearMemoryAddress<MDefinition*> addr;
     MDefinition* value;
     if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
         return false;
 
-    MAsmJSHeapAccess access(viewType, numElems);
-    access.setAlign(addr.align);
+    MWasmMemoryAccess access(viewType, addr.align, numElems);
 
     MDefinition* base = addr.base;
     if (!SetHeapAccessOffset(f, addr.offset, &access, &base))
         return false;
 
     f.storeSimdHeap(base, access, value);
     return true;
 }
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -19,16 +19,18 @@
 #include "asmjs/WasmJS.h"
 
 #include "asmjs/WasmCompile.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmModule.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/NativeObject-inl.h"
+
 using namespace js;
 using namespace js::wasm;
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
     if (!cx->jitSupportsFloatingPoint())
         return false;
@@ -36,16 +38,33 @@ wasm::HasCompilerSupport(ExclusiveContex
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     return false;
 #else
     return true;
 #endif
 }
 
 static bool
+CheckCompilerSupport(JSContext* cx)
+{
+    if (!HasCompilerSupport(cx)) {
+#ifdef JS_MORE_DETERMINISTIC
+        fprintf(stderr, "WebAssembly is not supported on the current device.\n");
+#endif
+        JS_ReportError(cx, "WebAssembly is not supported on the current device.");
+        return false;
+    }
+
+    return true;
+}
+
+// ============================================================================
+// (Temporary) Wasm class and static methods
+
+static bool
 Throw(JSContext* cx, const char* str)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, str);
     return false;
 }
 
 static bool
 GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v)
@@ -88,44 +107,43 @@ ImportFunctions(JSContext* cx, HandleObj
 
     return true;
 }
 
 bool
 wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
            MutableHandleWasmInstanceObject instanceObj)
 {
-    if (!HasCompilerSupport(cx)) {
-#ifdef JS_MORE_DETERMINISTIC
-        fprintf(stderr, "WebAssembly is not supported on the current device.\n");
-#endif
-        JS_ReportError(cx, "WebAssembly is not supported on the current device.");
+    if (!CheckCompilerSupport(cx))
         return false;
-    }
 
     Bytes bytecode;
     if (!bytecode.append((uint8_t*)code->viewDataEither().unwrap(), code->byteLength()))
         return false;
 
-    JS::AutoFilename filename;
-    if (!DescribeScriptedCaller(cx, &filename))
-        return false;
+    UniqueChars filename;
+    JS::AutoFilename af;
+    if (DescribeScriptedCaller(cx, &af)) {
+        filename = DuplicateString(cx, af.get());
+        if (!filename)
+            return false;
+    }
 
-    UniqueChars file = DuplicateString(filename.get());
-    if (!file)
-        return false;
-
-    UniqueModule module = Compile(cx, Move(file), Move(bytecode));
+    UniqueModule module = Compile(cx, Move(filename), Move(bytecode));
     if (!module)
         return false;
 
     Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
     if (!ImportFunctions(cx, importObj, module->importNames(), &funcImports))
         return false;
 
+    instanceObj.set(WasmInstanceObject::create(cx));
+    if (!instanceObj)
+        return false;
+
     return module->instantiate(cx, funcImports, nullptr, instanceObj);
 }
 
 static bool
 InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
 {
     MOZ_ASSERT(cx->runtime()->options().wasm());
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -198,61 +216,137 @@ js::InitWasmClass(JSContext* cx, HandleO
 
     if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
         return nullptr;
 
     global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
     return Wasm;
 }
 
+// ============================================================================
+// WebAssembly.Module class and methods
+
 const ClassOps WasmModuleObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WasmModuleObject::finalize
 };
 
 const Class WasmModuleObject::class_ =
 {
-    "WasmModuleObject",
+    "WebAssembly.Module",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS),
     &WasmModuleObject::classOps_,
 };
 
 /* static */ void
 WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
 {
     fop->delete_(&obj->as<WasmModuleObject>().module());
 }
 
 /* static */ WasmModuleObject*
-WasmModuleObject::create(ExclusiveContext* cx, UniqueModule module)
+WasmModuleObject::create(ExclusiveContext* cx, UniqueModule module, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
-    auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, nullptr);
+    auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     obj->setReservedSlot(MODULE_SLOT, PrivateValue((void*)module.release()));
     return obj;
 }
 
+static JSObject&
+GetConstructorPrototype(JSContext* cx, Native native, CallArgs args)
+{
+    RootedObject callee(cx, &args.callee());
+    MOZ_ASSERT(callee->as<JSFunction>().native() == native);
+
+    RootedValue proto(cx);
+    if (!GetProperty(cx, callee, callee, cx->names().prototype, &proto))
+        MOZ_CRASH("non-configurable data property of known constructor");
+
+    if (!proto.isObject())
+        MOZ_CRASH("readonly property of known constructor");
+
+    return proto.toObject();
+}
+
+static bool
+ModuleConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!ThrowIfNotConstructing(cx, args, "Module"))
+        return false;
+
+    if (!args.requireAtLeast(cx, "WebAssembly.Module", 1))
+        return false;
+
+    if (!args.get(0).isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    Bytes bytecode;
+    if (args[0].toObject().is<TypedArrayObject>()) {
+        TypedArrayObject& view = args[0].toObject().as<TypedArrayObject>();
+        if (!bytecode.append((uint8_t*)view.viewDataEither().unwrap(), view.byteLength()))
+            return false;
+    } else if (args[0].toObject().is<ArrayBufferObject>()) {
+        ArrayBufferObject& buffer = args[0].toObject().as<ArrayBufferObject>();
+        if (!bytecode.append(buffer.dataPointer(), buffer.byteLength()))
+            return false;
+    } else {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
+        return false;
+    }
+
+    UniqueChars filename;
+    JS::AutoFilename af;
+    if (DescribeScriptedCaller(cx, &af)) {
+        filename = DuplicateString(cx, af.get());
+        if (!filename)
+            return false;
+    }
+
+    if (!CheckCompilerSupport(cx))
+        return false;
+
+    UniqueModule module = Compile(cx, Move(filename), Move(bytecode));
+    if (!module)
+        return false;
+
+    RootedObject proto(cx, &GetConstructorPrototype(cx, ModuleConstructor, args));
+    RootedObject moduleObj(cx, WasmModuleObject::create(cx, Move(module), proto));
+    if (!moduleObj)
+        return false;
+
+    args.rval().setObject(*moduleObj);
+    return true;
+}
+
 Module&
 WasmModuleObject::module() const
 {
     MOZ_ASSERT(is<WasmModuleObject>());
     return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
+// ============================================================================
+// WebAssembly.Instance class and methods
+
 const ClassOps WasmInstanceObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
@@ -261,17 +355,17 @@ const ClassOps WasmInstanceObject::class
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     WasmInstanceObject::trace
 };
 
 const Class WasmInstanceObject::class_ =
 {
-    "WasmInstanceObject",
+    "WebAssembly.Instance",
     JSCLASS_DELAY_METADATA_BUILDER |
     JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS),
     &WasmInstanceObject::classOps_,
 };
 
 bool
 WasmInstanceObject::isNewborn() const
 {
@@ -289,20 +383,20 @@ WasmInstanceObject::finalize(FreeOp* fop
 /* static */ void
 WasmInstanceObject::trace(JSTracer* trc, JSObject* obj)
 {
     if (!obj->as<WasmInstanceObject>().isNewborn())
         obj->as<WasmInstanceObject>().instance().trace(trc);
 }
 
 /* static */ WasmInstanceObject*
-WasmInstanceObject::create(ExclusiveContext* cx)
+WasmInstanceObject::create(ExclusiveContext* cx, HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
-    auto obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, nullptr);
+    auto* obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     MOZ_ASSERT(obj->isNewborn());
     return obj;
 }
 
 void
@@ -314,21 +408,149 @@ WasmInstanceObject::init(UniqueInstance 
 }
 
 void
 WasmInstanceObject::initExportsObject(HandleObject exportObj)
 {
     initReservedSlot(EXPORTS_SLOT, ObjectValue(*exportObj));
 }
 
+static bool
+InstanceConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (!ThrowIfNotConstructing(cx, args, "Instance"))
+        return false;
+
+    if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1))
+        return false;
+
+    if (!args.get(0).isObject() || !args[0].toObject().is<WasmModuleObject>()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
+        return false;
+    }
+
+    const Module& module = args[0].toObject().as<WasmModuleObject>().module();
+
+    RootedObject importObj(cx);
+    if (!args.get(1).isUndefined()) {
+        if (!args[1].isObject()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
+            return false;
+        }
+        importObj = &args[1].toObject();
+    }
+
+    Rooted<FunctionVector> funcImports(cx, FunctionVector(cx));
+    if (!ImportFunctions(cx, importObj, module.importNames(), &funcImports))
+        return false;
+
+    RootedObject proto(cx, &GetConstructorPrototype(cx, InstanceConstructor, args));
+    RootedWasmInstanceObject instanceObj(cx, WasmInstanceObject::create(cx, proto));
+    if (!instanceObj)
+        return false;
+
+    if (!module.instantiate(cx, funcImports, nullptr, instanceObj))
+        return false;
+
+    args.rval().setObject(*instanceObj);
+    return true;
+}
+
 Instance&
 WasmInstanceObject::instance() const
 {
     MOZ_ASSERT(!isNewborn());
     return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
 }
 
 JSObject&
 WasmInstanceObject::exportsObject() const
 {
     MOZ_ASSERT(!isNewborn());
     return getReservedSlot(EXPORTS_SLOT).toObject();
 }
+
+// ============================================================================
+// WebAssembly class and static methods
+
+#if JS_HAS_TOSOURCE
+static bool
+WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setString(cx->names().WebAssembly);
+    return true;
+}
+#endif
+
+static const JSFunctionSpec WebAssembly_static_methods[] =
+{
+#if JS_HAS_TOSOURCE
+    JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
+#endif
+    JS_FS_END
+};
+
+const Class js::WebAssemblyClass =
+{
+    js_WebAssembly_str,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly)
+};
+
+template <class Class>
+static bool
+InitConstructor(JSContext* cx, HandleObject global, HandleObject wasm, const char* name,
+                Native native)
+{
+    RootedObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx, SingletonObject));
+    if (!proto)
+        return false;
+
+    RootedAtom className(cx, Atomize(cx, name, strlen(name)));
+    if (!className)
+        return false;
+
+    RootedFunction ctor(cx, NewNativeConstructor(cx, native, 1, className));
+    if (!ctor)
+        return false;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, proto))
+        return false;
+
+    RootedId id(cx, AtomToId(className));
+    RootedValue ctorValue(cx, ObjectValue(*ctor));
+    return DefineProperty(cx, wasm, id, ctorValue, nullptr, nullptr, 0);
+}
+
+JSObject*
+js::InitWebAssemblyClass(JSContext* cx, HandleObject global)
+{
+    MOZ_ASSERT(cx->runtime()->options().wasm());
+
+    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateObjectPrototype(cx));
+    if (!proto)
+        return nullptr;
+
+    RootedObject wasm(cx, NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject));
+    if (!wasm)
+        return nullptr;
+
+    if (!JS_DefineProperty(cx, global, js_WebAssembly_str, wasm, JSPROP_RESOLVING))
+        return nullptr;
+
+    // This property will be removed before the initial WebAssembly release.
+    if (!JS_DefineProperty(cx, wasm, "experimentalVersion", EncodingVersion, JSPROP_RESOLVING))
+        return nullptr;
+
+    if (!InitConstructor<WasmModuleObject>(cx, global, wasm, "Module", ModuleConstructor))
+        return nullptr;
+    if (!InitConstructor<WasmInstanceObject>(cx, global, wasm, "Instance", InstanceConstructor))
+        return nullptr;
+
+    if (!JS_DefineFunctions(cx, wasm, WebAssembly_static_methods))
+        return nullptr;
+
+    global->as<GlobalObject>().setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
+    return wasm;
+}
+
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -33,49 +33,61 @@ namespace wasm {
 
 class Module;
 class Instance;
 
 typedef UniquePtr<Module> UniqueModule;
 typedef UniquePtr<Instance> UniqueInstance;
 
 // Return whether WebAssembly can be compiled on this platform.
+// This must be checked and must be true to call any of the top-level wasm
+// eval/compile methods.
 
 bool
 HasCompilerSupport(ExclusiveContext* cx);
 
 // Compiles the given binary wasm module given the ArrayBufferObject
 // and links the module's imports with the given import object.
 
 MOZ_MUST_USE bool
 Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
      MutableHandle<WasmInstanceObject*> instanceObj);
 
 } // namespace wasm
 
-// The class of the Wasm global namespace object.
+// 'Wasm' and its one function 'instantiateModule' are transitional APIs and
+// will be removed (replaced by 'WebAssembly') before release.
 
 extern const Class WasmClass;
 
 JSObject*
 InitWasmClass(JSContext* cx, HandleObject global);
 
+// The class of the WebAssembly global namespace object.
+
+extern const Class WebAssemblyClass;
+
+JSObject*
+InitWebAssemblyClass(JSContext* cx, HandleObject global);
+
 // The class of wasm module object wrappers. Each WasmModuleObject owns a
 // wasm::Module. These objects are currently not exposed directly to JS.
 
 class WasmModuleObject : public NativeObject
 {
     static const unsigned MODULE_SLOT = 0;
     static const ClassOps classOps_;
     static void finalize(FreeOp* fop, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 1;
     static const Class class_;
 
-    static WasmModuleObject* create(ExclusiveContext* cx, wasm::UniqueModule module);
+    static WasmModuleObject* create(ExclusiveContext* cx,
+                                    wasm::UniqueModule module,
+                                    HandleObject proto = nullptr);
     wasm::Module& module() const;
 };
 
 typedef Rooted<WasmModuleObject*> RootedWasmModuleObject;
 typedef Handle<WasmModuleObject*> HandleWasmModuleObject;
 typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModuleObject;
 
 // The class of wasm instance object wrappers. Each WasmInstanceObject owns a
@@ -88,17 +100,18 @@ class WasmInstanceObject : public Native
     static const ClassOps classOps_;
     bool isNewborn() const;
     static void finalize(FreeOp* fop, JSObject* obj);
     static void trace(JSTracer* trc, JSObject* obj);
   public:
     static const unsigned RESERVED_SLOTS = 2;
     static const Class class_;
 
-    static WasmInstanceObject* create(ExclusiveContext* cx);
+    static WasmInstanceObject* create(ExclusiveContext* cx,
+                                      HandleObject proto = nullptr);
     void init(wasm::UniqueInstance module);
     void initExportsObject(HandleObject exportObj);
     wasm::Instance& instance() const;
     JSObject& exportsObject() const;
 };
 
 typedef GCVector<WasmInstanceObject*> WasmInstanceObjectVector;
 typedef Rooted<WasmInstanceObject*> RootedWasmInstanceObject;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -323,17 +323,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
              metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
 }
 
 bool
 Module::instantiate(JSContext* cx,
                     Handle<FunctionVector> funcImports,
                     Handle<ArrayBufferObjectMaybeShared*> asmJSHeap,
-                    MutableHandleWasmInstanceObject instanceObj) const
+                    HandleWasmInstanceObject instanceObj) const
 {
     MOZ_ASSERT(funcImports.length() == metadata_->imports.length());
     MOZ_ASSERT_IF(asmJSHeap, metadata_->isAsmJS());
 
     // asm.js module instantiation supplies its own heap, but for wasm, create
     // and initialize the heap if one is requested.
 
     Rooted<ArrayBufferObjectMaybeShared*> heap(cx, asmJSHeap);
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -189,17 +189,17 @@ class Module
     const Metadata& metadata() const { return *metadata_; }
     const ImportNameVector& importNames() const { return importNames_; }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      Handle<ArrayBufferObjectMaybeShared*> asmJSHeap,
-                     MutableHandle<WasmInstanceObject*> instanceObj) const;
+                     HandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
     static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
                                       UniquePtr<Module>* module,
                                       Metadata* maybeMetadata = nullptr);
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -353,17 +353,17 @@ struct macos_arm_context {
 #endif
 
 static uint8_t**
 ContextToPC(CONTEXT* context)
 {
 #ifdef JS_CODEGEN_NONE
     MOZ_CRASH();
 #else
-     return reinterpret_cast<uint8_t**>(&PC_sig(context));
+    return reinterpret_cast<uint8_t**>(&PC_sig(context));
 #endif
 }
 
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
 
 #if defined(JS_CODEGEN_X64)
 MOZ_COLD static void
 SetFPRegToNaN(size_t size, void* fp_reg)
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -606,60 +606,58 @@ wasm::GenerateJitExit(MacroAssembler& ma
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     static_assert(MaybeSavedGlobalReg == sizeof(void*), "stack frame accounting");
     masm.storePtr(GlobalReg, Address(masm.getStackPointer(), jitFrameBytes));
 #endif
 
     {
         // Enable Activation.
         //
-        // This sequence requires four registers, and needs to preserve the 'callee'
-        // register, so there are five live registers.
+        // This sequence requires three registers, and needs to preserve the 'callee'
+        // register, so there are four live registers.
         MOZ_ASSERT(callee == AsmJSIonExitRegCallee);
         Register reg0 = AsmJSIonExitRegE0;
         Register reg1 = AsmJSIonExitRegE1;
         Register reg2 = AsmJSIonExitRegE2;
-        Register reg3 = AsmJSIonExitRegE3;
 
         // The following is inlined:
         //   JSContext* cx = activation->cx();
-        //   Activation* act = cx->runtime()->activation();
+        //   Activation* act = cx->activation();
         //   act.active_ = true;
-        //   act.prevJitTop_ = cx->runtime()->jitTop;
-        //   act.prevJitActivation_ = cx->runtime()->jitActivation;
-        //   cx->runtime()->jitActivation = act;
-        //   act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
-        //   cx->runtime()->profilingActivation_ = act;
+        //   act.prevJitTop_ = cx->jitTop;
+        //   act.prevJitActivation_ = cx->jitActivation;
+        //   cx->jitActivation = act;
+        //   act.prevProfilingActivation_ = cx->profilingActivation;
+        //   cx->profilingActivation_ = act;
         // On the ARM store8() uses the secondScratchReg (lr) as a temp.
-        size_t offsetOfActivation = JSRuntime::offsetOfActivation();
-        size_t offsetOfJitTop = offsetof(JSRuntime, jitTop);
-        size_t offsetOfJitActivation = offsetof(JSRuntime, jitActivation);
-        size_t offsetOfProfilingActivation = JSRuntime::offsetOfProfilingActivation();
+        size_t offsetOfActivation = JSContext::offsetOfActivation();
+        size_t offsetOfJitTop = offsetof(JSContext, jitTop);
+        size_t offsetOfJitActivation = offsetof(JSContext, jitActivation);
+        size_t offsetOfProfilingActivation = JSContext::offsetOfProfilingActivation();
         masm.loadWasmActivation(reg0);
-        masm.loadPtr(Address(reg0, WasmActivation::offsetOfContext()), reg3);
-        masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
+        masm.loadPtr(Address(reg0, WasmActivation::offsetOfContext()), reg0);
         masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
 
         //   act.active_ = true;
         masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8()));
 
-        //   act.prevJitTop_ = cx->runtime()->jitTop;
+        //   act.prevJitTop_ = cx->jitTop;
         masm.loadPtr(Address(reg0, offsetOfJitTop), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
 
-        //   act.prevJitActivation_ = cx->runtime()->jitActivation;
+        //   act.prevJitActivation_ = cx->jitActivation;
         masm.loadPtr(Address(reg0, offsetOfJitActivation), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitActivation()));
-        //   cx->runtime()->jitActivation = act;
+        //   cx->jitActivation = act;
         masm.storePtr(reg1, Address(reg0, offsetOfJitActivation));
 
-        //   act.prevProfilingActivation_ = cx->runtime()->profilingActivation;
+        //   act.prevProfilingActivation_ = cx->profilingActivation;
         masm.loadPtr(Address(reg0, offsetOfProfilingActivation), reg2);
         masm.storePtr(reg2, Address(reg1, Activation::offsetOfPrevProfiling()));
-        //   cx->runtime()->profilingActivation_ = act;
+        //   cx->profilingActivation_ = act;
         masm.storePtr(reg1, Address(reg0, offsetOfProfilingActivation));
     }
 
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
     masm.callJitNoProfiler(callee);
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
 
     {
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -438,30 +438,96 @@ PromiseObject::onSettled(JSContext* cx)
             PROMISE_IS_HANDLED_STATE_HANDLED)
     {
         cx->runtime()->addUnhandledRejectedPromise(cx, promise);
     }
 
     JS::dbg::onPromiseSettled(cx, promise);
 }
 
+enum ReactionJobSlots {
+    ReactionJobSlot_Handler = 0,
+    ReactionJobSlot_JobData,
+};
+
+enum ReactionJobDataSlots {
+    ReactionJobDataSlot_HandlerArg = 0,
+    ReactionJobDataSlot_ResolveHook,
+    ReactionJobDataSlot_RejectHook,
+    ReactionJobDataSlotsCount,
+};
+
 // ES6, 25.4.2.1.
-bool
+/**
+ * Callback triggering the fulfill/reject reaction for a resolved Promise,
+ * to be invoked by the embedding during its processing of the Promise job
+ * queue.
+ *
+ * See http://www.ecma-international.org/ecma-262/6.0/index.html#sec-jobs-and-job-queues
+ *
+ * A PromiseReactionJob is set as the native function of an extended
+ * JSFunction object, with all information required for the job's
+ * execution stored in the function's extended slots.
+ *
+ * Usage of the function's extended slots is as follows:
+ * ReactionJobSlot_Handler: The handler to use as the Promise reaction.
+ *                          This can be PROMISE_HANDLER_IDENTITY,
+ *                          PROMISE_HANDLER_THROWER, or a callable. In the
+ *                          latter case, it's guaranteed to be an object from
+ *                          the same compartment as the PromiseReactionJob.
+ * ReactionJobSlot_JobData: JobData - a, potentially CCW-wrapped, dense list
+ *                          containing data required for proper execution of
+ *                          the reaction.
+ *
+ * The JobData list has the following entries:
+ * ReactionJobDataSlot_HandlerArg: Value passed as argument when invoking the
+ *                                 reaction handler.
+ * ReactionJobDataSlot_ResolveHook: The Promise's resolve hook, invoked if the
+ *                                  handler is PROMISE_HANDLER_IDENTITY or
+ *                                  upon successful execution of a callable
+ *                                  handler.
+ *  ReactionJobDataSlot_RejectHook: The Promise's reject hook, invoked if the
+ *                                  handler is PROMISE_HANDLER_THROWER or if
+ *                                  execution of a callable handler aborts
+ *                                  abnormally.
+ */
+static bool
 PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
+
     RootedFunction job(cx, &args.callee().as<JSFunction>());
-    RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
+
+    RootedValue handlerVal(cx, job->getExtendedSlot(ReactionJobSlot_Handler));
+    RootedObject jobDataObj(cx, &job->getExtendedSlot(ReactionJobSlot_JobData).toObject());
 
-    RootedValue argument(cx, jobArgs->getDenseElement(1));
+    // To ensure that the embedding ends up with the right entry global, we're
+    // guaranteeing that the reaction job function gets created in the same
+    // compartment as the handler function. That's not necessarily the global
+    // that the job was triggered from, though. To be able to find the
+    // triggering global, we always create the jobArgs object in that global
+    // and wrap it into the handler's. So to go back, we check if jobArgsObj
+    // is a wrapper and if so, unwrap it, enter its compartment, and wrap
+    // the handler into that compartment.
+    //
+    // See the doc comment for PromiseReactionJob for how this information is
+    // stored.
+    mozilla::Maybe<AutoCompartment> ac;
+    if (IsWrapper(jobDataObj)) {
+        jobDataObj = UncheckedUnwrap(jobDataObj);
+        ac.emplace(cx, jobDataObj);
+        if (!cx->compartment()->wrap(cx, &handlerVal))
+            return false;
+    }
+    RootedNativeObject jobData(cx, &jobDataObj->as<NativeObject>());
+    RootedValue argument(cx, jobData->getDenseElement(ReactionJobDataSlot_HandlerArg));
 
     // Step 1 (omitted).
 
     // Steps 2-3.
-    RootedValue handlerVal(cx, jobArgs->getDenseElement(0));
     RootedValue handlerResult(cx);
     bool shouldReject = false;
 
     // Steps 4-7.
     if (handlerVal.isNumber()) {
         int32_t handlerNum = int32_t(handlerVal.toNumber());
         // Step 4.
         if (handlerNum == PROMISE_HANDLER_IDENTITY) {
@@ -481,41 +547,173 @@ PromiseReactionJob(JSContext* cx, unsign
             // Not much we can do about uncatchable exceptions, so just bail
             // for those.
             if (!cx->isExceptionPending() || !GetAndClearException(cx, &handlerResult))
                 return false;
         }
     }
 
     // Steps 7-9.
+    size_t hookSlot = shouldReject
+                      ? ReactionJobDataSlot_RejectHook
+                      : ReactionJobDataSlot_ResolveHook;
+    RootedObject callee(cx, &jobData->getDenseElement(hookSlot).toObject());
+
     FixedInvokeArgs<1> args2(cx);
     args2[0].set(handlerResult);
-    RootedValue calleeOrRval(cx);
-    if (shouldReject) {
-        calleeOrRval = jobArgs->getDenseElement(3);
-    } else {
-        calleeOrRval = jobArgs->getDenseElement(2);
-    }
+    RootedValue calleeOrRval(cx, ObjectValue(*callee));
     bool result = Call(cx, calleeOrRval, UndefinedHandleValue, args2, &calleeOrRval);
 
     args.rval().set(calleeOrRval);
     return result;
 }
 
+bool
+EnqueuePromiseReactionJob(JSContext* cx, HandleValue handler_, HandleValue handlerArg,
+                          HandleObject resolve, HandleObject reject,
+                          HandleObject promise_, HandleObject objectFromIncumbentGlobal_)
+{
+    // Create a dense array to hold the data needed for the reaction job to
+    // work.
+    // See doc comment for PromiseReactionJob for layout details.
+    RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ReactionJobDataSlotsCount));
+    if (!data ||
+        data->ensureDenseElements(cx, 0, ReactionJobDataSlotsCount) != DenseElementResult::Success)
+    {
+        return false;
+    }
+
+    // Store the handler argument.
+    data->setDenseElement(ReactionJobDataSlot_HandlerArg, handlerArg);
+
+    // Store the resolve hook.
+    data->setDenseElement(ReactionJobDataSlot_ResolveHook, ObjectValue(*resolve));
+
+    // Store the reject hook.
+    data->setDenseElement(ReactionJobDataSlot_RejectHook, ObjectValue(*reject));
+
+    RootedValue dataVal(cx, ObjectValue(*data));
+
+    // Re-rooting because we might need to unwrap it.
+    RootedValue handler(cx, handler_);
+
+    // If we have a handler callback, we enter that handler's compartment so
+    // that the promise reaction job function is created in that compartment.
+    // That guarantees that the embedding ends up with the right entry global.
+    // This is relevant for some html APIs like fetch that derive information
+    // from said global.
+    mozilla::Maybe<AutoCompartment> ac;
+    if (handler.isObject()) {
+        RootedObject handlerObj(cx, &handler.toObject());
+
+        // The unwrapping has to be unchecked because we specifically want to
+        // be able to use handlers with wrappers that would only allow calls.
+        // E.g., it's ok to have a handler from a chrome compartment in a
+        // reaction to a content compartment's Promise instance.
+        handlerObj = UncheckedUnwrap(handlerObj);
+        MOZ_ASSERT(handlerObj);
+        ac.emplace(cx, handlerObj);
+        handler = ObjectValue(*handlerObj);
+
+        // We need to wrap the |data| array to store it on the job function.
+        if (!cx->compartment()->wrap(cx, &dataVal))
+            return false;
+    }
+
+    // Create the JS function to call when the job is triggered.
+    RootedAtom funName(cx, cx->names().empty);
+    RootedFunction job(cx, NewNativeFunction(cx, PromiseReactionJob, 0, funName,
+                                             gc::AllocKind::FUNCTION_EXTENDED));
+    if (!job)
+        return false;
+
+    // Store the handler and the data array on the reaction job.
+    job->setExtendedSlot(ReactionJobSlot_Handler, handler);
+    job->setExtendedSlot(ReactionJobSlot_JobData, dataVal);
+
+    // When using JS::AddPromiseReactions, no actual promise is created, so we
+    // might not have one here.
+    // If we do, Wrap it in case we entered the handler's compartment above,
+    // because we should pass objects from a single compartment to the
+    // enqueuePromiseJob callback.
+    RootedObject promise(cx, promise_);
+    if (!cx->compartment()->wrap(cx, &promise))
+        return false;
+
+    // Using objectFromIncumbentGlobal, we can derive the incumbent global by
+    // unwrapping and then getting the global. This is very convoluted, but
+    // much better than having to store the original global as a private value
+    // because we couldn't wrap it to store it as a normal JS value.
+    RootedObject global(cx);
+    RootedObject objectFromIncumbentGlobal(cx, objectFromIncumbentGlobal_);
+    if (objectFromIncumbentGlobal) {
+        objectFromIncumbentGlobal = CheckedUnwrap(objectFromIncumbentGlobal);
+        MOZ_ASSERT(objectFromIncumbentGlobal);
+        global = &objectFromIncumbentGlobal->global();
+    }
+
+    // Note: the global we pass here might be from a different compartment
+    // than job and promise. While it's somewhat unusual to pass objects
+    // from multiple compartments, in this case we specifically need the
+    // global to be unwrapped because wrapping and unwrapping aren't
+    // necessarily symmetric for globals.
+    return cx->runtime()->enqueuePromiseJob(cx, job, promise, global);
+}
+
+enum ThenableJobSlots {
+    ThenableJobSlot_Handler = 0,
+    ThenableJobSlot_JobData,
+};
+
+enum ThenableJobDataSlots {
+    ThenableJobDataSlot_Promise = 0,
+    ThenableJobDataSlot_Thenable,
+    ThenableJobDataSlotsCount,
+};
 // ES6, 25.4.2.2.
-bool
+/**
+ * Callback for resolving a thenable, to be invoked by the embedding during
+ * its processing of the Promise job queue.
+ *
+ * See http://www.ecma-international.org/ecma-262/6.0/index.html#sec-jobs-and-job-queues
+ *
+ * A PromiseResolveThenableJob is set as the native function of an extended
+ * JSFunction object, with all information required for the job's
+ * execution stored in the function's extended slots.
+ *
+ * Usage of the function's extended slots is as follows:
+ * ThenableJobSlot_Handler: The handler to use as the Promise reaction.
+ *                          This can be PROMISE_HANDLER_IDENTITY,
+ *                          PROMISE_HANDLER_THROWER, or a callable. In the
+ *                          latter case, it's guaranteed to be an object
+ *                          from the same compartment as the
+ *                          PromiseReactionJob.
+ * ThenableJobSlot_JobData: JobData - a, potentially CCW-wrapped, dense list
+ *                          containing data required for proper execution of
+ *                          the reaction.
+ *
+ * The JobData list has the following entries:
+ * ThenableJobDataSlot_Promise: The Promise to resolve using the given
+ *                              thenable.
+ * ThenableJobDataSlot_Thenable: The thenable to use as the receiver when
+ *                               calling the `then` function.
+ */
+static bool
 PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    RootedFunction job(cx, &args.callee().as<JSFunction>());
-    RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
 
-    RootedValue promise(cx, jobArgs->getDenseElement(2));
-    RootedValue then(cx, jobArgs->getDenseElement(0));
-    RootedValue thenable(cx, jobArgs->getDenseElement(1));
+    RootedFunction job(cx, &args.callee().as<JSFunction>());
+    RootedValue then(cx, job->getExtendedSlot(ThenableJobSlot_Handler));
+    MOZ_ASSERT(!IsWrapper(&then.toObject()));
+    RootedNativeObject jobArgs(cx, &job->getExtendedSlot(ThenableJobSlot_JobData)
+                                    .toObject().as<NativeObject>());
+
+    RootedValue promise(cx, jobArgs->getDenseElement(ThenableJobDataSlot_Promise));
+    RootedValue thenable(cx, jobArgs->getDenseElement(ThenableJobDataSlot_Thenable));
 
     // Step 1.
     RootedValue resolveVal(cx);
     RootedValue rejectVal(cx);
     if (!CreateResolvingFunctions(cx, promise, &resolveVal, &rejectVal))
         return false;
 
     // Step 2.
@@ -533,16 +731,74 @@ PromiseResolveThenableJob(JSContext* cx,
         return false;
 
     FixedInvokeArgs<1> rejectArgs(cx);
     rejectArgs[0].set(rval);
 
     return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
 }
 
+bool
+EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve_,
+                                 HandleValue thenable_, HandleValue thenVal)
+{
+    // Need to re-root these to enable wrapping them below.
+    RootedValue promiseToResolve(cx, promiseToResolve_);
+    RootedValue thenable(cx, thenable_);
+
+    // We enter the `then` callable's compartment so that the job function is
+    // created in that compartment.
+    // That guarantees that the embedding ends up with the right entry global.
+    // This is relevant for some html APIs like fetch that derive information
+    // from said global.
+    RootedObject then(cx, CheckedUnwrap(&thenVal.toObject()));
+    AutoCompartment ac(cx, then);
+
+    RootedAtom funName(cx, cx->names().empty);
+    if (!funName)
+        return false;
+    RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName,
+                                             gc::AllocKind::FUNCTION_EXTENDED));
+    if (!job)
+        return false;
+
+    // Store the `then` function on the callback.
+    job->setExtendedSlot(ThenableJobSlot_Handler, ObjectValue(*then));
+
+    // Create a dense array to hold the data needed for the reaction job to
+    // work.
+    // See the doc comment for PromiseResolveThenableJob for the layout.
+    RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataSlotsCount));
+    if (!data ||
+        data->ensureDenseElements(cx, 0, ThenableJobDataSlotsCount) != DenseElementResult::Success)
+    {
+        return false;
+    }
+
+    // Wrap and set the `promiseToResolve` argument.
+    if (!cx->compartment()->wrap(cx, &promiseToResolve))
+        return false;
+    data->setDenseElement(ThenableJobDataSlot_Promise, promiseToResolve);
+    // At this point the promise is guaranteed to be wrapped into the job's
+    // compartment.
+    RootedObject promise(cx, &promiseToResolve.toObject());
+
+    // Wrap and set the `thenable` argument.
+    MOZ_ASSERT(thenable.isObject());
+    if (!cx->compartment()->wrap(cx, &thenable))
+        return false;
+    data->setDenseElement(ThenableJobDataSlot_Thenable, thenable);
+
+    // Store the data array on the reaction job.
+    job->setExtendedSlot(ThenableJobSlot_JobData, ObjectValue(*data));
+
+    RootedObject incumbentGlobal(cx, cx->runtime()->getIncumbentGlobal(cx));
+    return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal);
+}
+
 } // namespace js
 
 static JSObject*
 CreatePromisePrototype(JSContext* cx, JSProtoKey key)
 {
     return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
 }
 
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -62,17 +62,42 @@ class PromiseObject : public NativeObjec
     }
     void markAsReported() {
         MOZ_ASSERT(getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() ==
                    PROMISE_IS_HANDLED_STATE_UNHANDLED);
         setFixedSlot(PROMISE_IS_HANDLED_SLOT, Int32Value(PROMISE_IS_HANDLED_STATE_REPORTED));
     }
 };
 
-// ES6, 25.4.2.1.
-bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
+/**
+ * Tells the embedding to enqueue a Promise reaction job, based on six
+ * parameters:
+ * reaction handler - The callback to invoke for this job.
+   argument - The first and only argument to pass to the handler.
+   resolve - The Promise cabability's resolve hook, called upon normal
+             completion of the handler.
+   reject -  The Promise cabability's reject hook, called if the handler
+             throws.
+   promise - The associated Promise, or null for some internal uses.
+   objectFromIncumbentGlobal - An object from the global that was the
+                               incumbent global when the Promise reaction job
+                               was created (not enqueued). Not the global
+                               itself because unwrapping that might unwrap an
+                               inner to an outer window, which we never want
+                               to happen.
+ */
+bool EnqueuePromiseReactionJob(JSContext* cx, HandleValue handler, HandleValue handlerArg,
+                               HandleObject resolve, HandleObject reject,
+                               HandleObject promise, HandleObject objectFromIncumbentGlobal);
 
-// ES6, 25.4.2.2.
-bool PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp);
+/**
+ * Tells the embedding to enqueue a Promise resolve thenable job, based on six
+ * parameters:
+ * promiseToResolve - The promise to resolve, obviously.
+ * thenable - The thenable to resolve the Promise with.
+ * then - The `then` function to invoke with the `thenable` as the receiver.
+ */
+bool EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve,
+                                      HandleValue thenable, HandleValue then);
 
 } // namespace js
 
 #endif /* builtin_Promise_h */
--- a/js/src/builtin/Promise.js
+++ b/js/src/builtin/Promise.js
@@ -80,17 +80,17 @@ function CreateResolvingFunctions(promis
             if (unwrap) {
                 return callFunction(CallPromiseMethodIfWrapped, promise, resolution,
                                     "FulfillUnwrappedPromise");
             }
             return FulfillPromise(promise, resolution);
         }
 
         // Step 12.
-        EnqueuePromiseResolveThenableJob(promise, resolution, then);
+        _EnqueuePromiseResolveThenableJob(promise, resolution, then);
 
         // Step 13.
         return undefined;
     }
 
     // Steps 5-7.
     // ES6, 25.4.1.3.2.
     function reject(reason) {
@@ -222,32 +222,25 @@ function TriggerPromiseReactions(reactio
     // Step 2 (implicit).
 }
 
 // ES2016, February 12 draft 25.4.1.9, implemented in SelfHosting.cpp.
 
 // ES6, 25.4.2.1.
 function EnqueuePromiseReactionJob(reaction, argument) {
     let capabilities = reaction.capabilities;
-    _EnqueuePromiseReactionJob([reaction.handler,
-                                argument,
-                                capabilities.resolve,
-                                capabilities.reject
-                               ],
-                               capabilities.promise);
+    _EnqueuePromiseReactionJob(reaction.handler,
+                               argument,
+                               capabilities.resolve,
+                               capabilities.reject,
+                               capabilities.promise,
+                               reaction.incumbentGlobal || null);
 }
 
-// ES6, 25.4.2.2.
-function EnqueuePromiseResolveThenableJob(promiseToResolve, thenable, then) {
-    _EnqueuePromiseResolveThenableJob([then,
-                                       thenable,
-                                       promiseToResolve
-                                      ],
-                                      promiseToResolve);
-}
+// ES6, 25.4.2.2. (Implemented in C++).
 
 // ES6, 25.4.3.1. (Implemented in C++).
 
 // ES7 2016-01-21 draft, 25.4.4.1.
 function Promise_static_all(iterable) {
     // Step 1.
     let C = this;
 
@@ -872,28 +865,31 @@ function PerformPromiseThen(promise, onF
     // Step 3.
     if (!IsCallable(onFulfilled))
         onFulfilled = PROMISE_HANDLER_IDENTITY;
 
     // Step 4.
     if (!IsCallable(onRejected))
         onRejected = PROMISE_HANDLER_THROWER;
 
+    let incumbentGlobal = _GetObjectFromIncumbentGlobal();
     // Step 5.
     let fulfillReaction = {
         __proto__: PromiseReactionRecordProto,
         capabilities: resultCapability,
-        handler: onFulfilled
+        handler: onFulfilled,
+        incumbentGlobal
     };
 
     // Step 6.
     let rejectReaction = {
         __proto__: PromiseReactionRecordProto,
         capabilities: resultCapability,
-        handler: onRejected
+        handler: onRejected,
+        incumbentGlobal
     };
 
     // Step 7.
     let state = GetPromiseState(promise);
     if (state === PROMISE_STATE_PENDING) {
         // Step 7.a.
         let fulfillReactions = UnsafeGetObjectFromReservedSlot(promise,
                                                                PROMISE_FULFILL_REACTIONS_SLOT);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1276,19 +1276,16 @@ OOMTest(JSContext* cx, unsigned argc, Va
     if (fuzzingSafe)
         expectExceptionOnFailure = false;
 
     if (disableOOMFunctions) {
         args.rval().setUndefined();
         return true;
     }
 
-    MOZ_ASSERT(!cx->isExceptionPending());
-    cx->runtime()->hadOutOfMemory = false;
-
     RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
 
     bool verbose = EnvVarIsDefined("OOM_VERBOSE");
 
     unsigned threadStart = oom::THREAD_TYPE_MAIN;
     unsigned threadEnd = oom::THREAD_TYPE_MAX;
 
     // Test a single thread type if specified by the OOM_THREAD environment variable.
@@ -1298,16 +1295,26 @@ OOMTest(JSContext* cx, unsigned argc, Va
             JS_ReportError(cx, "OOM_THREAD value out of range.");
             return false;
         }
 
         threadStart = threadOption;
         threadEnd = threadOption + 1;
     }
 
+    JSRuntime* rt = cx->runtime();
+    if (rt->runningOOMTest) {
+        JS_ReportError(cx, "Nested call to oomTest() is not allowed.");
+        return false;
+    }
+    rt->runningOOMTest = true;
+
+    MOZ_ASSERT(!cx->isExceptionPending());
+    rt->hadOutOfMemory = false;
+
     JS_SetGCZeal(cx->runtime(), 0, JS_DEFAULT_ZEAL_FREQ);
 
     for (unsigned thread = threadStart; thread < threadEnd; thread++) {
         if (verbose)
             fprintf(stderr, "thread %d\n", thread);
 
         HelperThreadState().waitForAllThreads();
         js::oom::targetThread = thread;
@@ -1353,16 +1360,17 @@ OOMTest(JSContext* cx, unsigned argc, Va
             allocation++;
         } while (handledOOM);
 
         if (verbose) {
             fprintf(stderr, "  finished after %d allocations\n", allocation - 2);
         }
     }
 
+    rt->runningOOMTest = false;
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 #ifdef SPIDERMONKEY_PROMISE
 static bool
 SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -190,16 +190,17 @@ def main(argv):
         options.read_tests = options.retest
         options.write_failures = options.retest
 
     test_list = []
     read_all = True
 
     # Forbid running several variants of the same asmjs test, when debugging.
     options.can_test_also_noasmjs = not options.debugger
+    options.can_test_also_wasm_baseline = not options.debugger
 
     if test_args:
         read_all = False
         for arg in test_args:
             test_list += jittests.find_tests(arg)
 
     if options.read_tests:
         read_all = False
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1282986.js
@@ -0,0 +1,17 @@
+if (!('oomTest' in this))
+    quit();
+
+var lfLogBuffer = `
+evalInWorker(\`
+        try { oomAfterAllocations(2); } catch(e) {}
+    \`);
+`;
+loadFile("");
+loadFile(lfLogBuffer);
+function loadFile(lfVarx) {
+    oomTest(function() {
+        let m = parseModule(lfVarx);
+        m.declarationInstantiation();
+        m.evaluation();
+    });
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -0,0 +1,120 @@
+load(libdir + 'wasm.js');
+load(libdir + 'asserts.js');
+
+const emptyModule = wasmTextToBinary('(module)');
+
+// 'WebAssembly' property on global object
+const wasmDesc = Object.getOwnPropertyDescriptor(this, 'WebAssembly');
+assertEq(typeof wasmDesc.value, "object");
+assertEq(wasmDesc.writable, true);
+assertEq(wasmDesc.enumerable, false);
+assertEq(wasmDesc.configurable, true);
+
+// 'WebAssembly' object
+assertEq(WebAssembly, wasmDesc.value);
+assertEq(String(WebAssembly), "[object WebAssembly]");
+
+// 'WebAssembly.Module' property
+const moduleDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Module');
+assertEq(typeof moduleDesc.value, "function");
+assertEq(moduleDesc.writable, true);
+assertEq(moduleDesc.enumerable, false);
+assertEq(moduleDesc.configurable, true);
+
+// 'WebAssembly.Module' constructor function
+const Module = WebAssembly.Module;
+assertEq(Module, moduleDesc.value);
+assertEq(Module.length, 1);
+assertEq(Module.name, "Module");
+assertErrorMessage(() => Module(), TypeError, /constructor without new is forbidden/);
+assertErrorMessage(() => new Module(1), TypeError, "first argument must be an ArrayBuffer or typed array object");
+assertErrorMessage(() => new Module({}), TypeError, "first argument must be an ArrayBuffer or typed array object");
+assertErrorMessage(() => new Module(new Uint8Array()), /* TODO: WebAssembly.CompileError */ TypeError, /wasm validation error/);
+assertErrorMessage(() => new Module(new ArrayBuffer()), /* TODO: WebAssembly.CompileError */ TypeError, /wasm validation error/);
+assertEq(new Module(emptyModule) instanceof Module, true);
+assertEq(new Module(emptyModule.buffer) instanceof Module, true);
+
+// 'WebAssembly.Module.prototype' property
+const moduleProtoDesc = Object.getOwnPropertyDescriptor(Module, 'prototype');
+assertEq(typeof moduleProtoDesc.value, "object");
+assertEq(moduleProtoDesc.writable, false);
+assertEq(moduleProtoDesc.enumerable, false);
+assertEq(moduleProtoDesc.configurable, false);
+
+// 'WebAssembly.Module.prototype' object
+const moduleProto = Module.prototype;
+assertEq(moduleProto, moduleProtoDesc.value);
+assertEq(String(moduleProto), "[object Object]");
+assertEq(Object.getPrototypeOf(moduleProto), Object.prototype);
+
+// 'WebAssembly.Module' instance objects
+const m1 = new Module(emptyModule);
+assertEq(typeof m1, "object");
+assertEq(String(m1), "[object WebAssembly.Module]");
+assertEq(Object.getPrototypeOf(m1), moduleProto);
+
+// 'WebAssembly.Instance' property
+const instanceDesc = Object.getOwnPropertyDescriptor(WebAssembly, 'Instance');
+assertEq(typeof instanceDesc.value, "function");
+assertEq(instanceDesc.writable, true);
+assertEq(instanceDesc.enumerable, false);
+assertEq(instanceDesc.configurable, true);
+
+// 'WebAssembly.Instance' constructor function
+const Instance = WebAssembly.Instance;
+assertEq(Instance, instanceDesc.value);
+assertEq(Instance.length, 1);
+assertEq(Instance.name, "Instance");
+assertErrorMessage(() => Instance(), TypeError, /constructor without new is forbidden/);
+assertErrorMessage(() => new Instance(1), TypeError, "first argument must be a WebAssembly.Module");
+assertErrorMessage(() => new Instance({}), TypeError, "first argument must be a WebAssembly.Module");
+assertErrorMessage(() => new Instance(m1, null), TypeError, "second argument, if present, must be an object");
+assertEq(new Instance(m1) instanceof Instance, true);
+assertEq(new Instance(m1, {}) instanceof Instance, true);
+
+// 'WebAssembly.Instance.prototype' property
+const instanceProtoDesc = Object.getOwnPropertyDescriptor(Instance, 'prototype');
+assertEq(typeof instanceProtoDesc.value, "object");
+assertEq(instanceProtoDesc.writable, false);
+assertEq(instanceProtoDesc.enumerable, false);
+assertEq(instanceProtoDesc.configurable, false);
+
+// 'WebAssembly.Instance.prototype' object
+const instanceProto = Instance.prototype;
+assertEq(instanceProto, instanceProtoDesc.value);
+assertEq(String(instanceProto), "[object Object]");
+assertEq(Object.getPrototypeOf(instanceProto), Object.prototype);
+
+// 'WebAssembly.Instance' instance objects
+const i1 = new Instance(m1);
+assertEq(typeof i1, "object");
+assertEq(String(i1), "[object WebAssembly.Instance]");
+assertEq(Object.getPrototypeOf(i1), instanceProto);
+
+// 'WebAssembly.Instance' 'exports' property
+const exportsDesc = Object.getOwnPropertyDescriptor(i1, 'exports');
+assertEq(typeof exportsDesc.value, "object");
+assertEq(exportsDesc.writable, true);
+assertEq(exportsDesc.enumerable, true);
+assertEq(exportsDesc.configurable, true);
+
+// Exports object:
+// Note: at some point the exports object should become an ES6 module namespace
+// exotic object. For now, don't probe too hard on the property descriptors or
+// the exports object itself.
+
+const e1 = i1.exports;
+assertEq(e1, exportsDesc.value);
+assertEq(Object.keys(e1).length, 0);
+
+var code = wasmTextToBinary('(module (func) (export "foo" 0))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "foo");
+assertEq(e.foo(), undefined);
+
+var code = wasmTextToBinary('(module (func) (export "foo" 0) (export "bar" 0))');
+var e = new Instance(new Module(code)).exports;
+assertEq(Object.keys(e).join(), "foo,bar");
+assertEq(e.foo(), undefined);
+assertEq(e.bar(), undefined);
+assertEq(e.foo, e.bar);
--- a/js/src/jit-test/tests/wasm/spec.js
+++ b/js/src/jit-test/tests/wasm/spec.js
@@ -293,17 +293,18 @@ function exec(e) {
         let caught = false;
         let errMsg = e.list[2];
         assert(errMsg.quoted, "assert_trap second argument must be a string");
         errMsg.quoted = false;
         try {
             exec(e.list[1]);
         } catch(err) {
             caught = true;
-            assert(err.toString().indexOf(errMsg) !== -1, `expected error message "${errMsg}", got "${err}"`);
+            if (err.toString().indexOf(errMsg) === -1)
+                warn(`expected error message "${errMsg}", got "${err}"`);
         }
         assert(caught, "assert_trap exception not caught");
         return;
     }
 
     if(!handleNonStandard(exprName, e)) {
         assert(false, "NYI: " + e);
     }
--- a/js/src/jit-test/tests/wasm/spec/func_ptrs.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/func_ptrs.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO Pass the table index in the error message?
-quit();
 var importedArgs = ['func_ptrs.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -95,19 +95,19 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* l
     if (base->isRecoveredOnBailout())
         return;
 
     MEffectiveAddress* eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement);
     last->replaceAllUsesWith(eaddr);
     last->block()->insertAfter(last, eaddr);
 }
 
-template<typename MAsmJSHeapAccessType>
+template<typename MWasmMemoryAccessType>
 bool
-EffectiveAddressAnalysis::tryAddDisplacement(MAsmJSHeapAccessType* ins, int32_t o)
+EffectiveAddressAnalysis::tryAddDisplacement(MWasmMemoryAccessType* ins, int32_t o)
 {
     // Compute the new offset. Check for overflow.
     uint32_t oldOffset = ins->offset();
     uint32_t newOffset = oldOffset + o;
     if (o < 0 ? (newOffset >= oldOffset) : (newOffset < oldOffset))
         return false;
 
     // Compute the new offset to the end of the access. Check for overflow
@@ -122,19 +122,19 @@ EffectiveAddressAnalysis::tryAddDisplace
     if (size_t(newEnd) > range)
         return false;
 
     // Everything checks out. This is the new offset.
     ins->setOffset(newOffset);
     return true;
 }
 
-template<typename MAsmJSHeapAccessType>
+template<typename MWasmMemoryAccessType>
 void
-EffectiveAddressAnalysis::analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins)
+EffectiveAddressAnalysis::analyzeAsmHeapAccess(MWasmMemoryAccessType* ins)
 {
     MDefinition* base = ins->base();
 
     if (base->isConstant()) {
         // Look for heap[i] where i is a constant offset, and fold the offset.
         // By doing the folding now, we simplify the task of codegen; the offset
         // is always the address mode immediate. This also allows it to avoid
         // a situation where the sum of a constant pointer value and a non-zero
--- a/js/src/jit/EffectiveAddressAnalysis.h
+++ b/js/src/jit/EffectiveAddressAnalysis.h
@@ -14,21 +14,21 @@ namespace jit {
 
 class MIRGraph;
 
 class EffectiveAddressAnalysis
 {
     MIRGenerator* mir_;
     MIRGraph& graph_;
 
-    template<typename MAsmJSHeapAccessType>
-    MOZ_MUST_USE bool tryAddDisplacement(MAsmJSHeapAccessType* ins, int32_t o);
+    template<typename MWasmMemoryAccessType>
+    MOZ_MUST_USE bool tryAddDisplacement(MWasmMemoryAccessType* ins, int32_t o);
 
-    template<typename MAsmJSHeapAccessType>
-    void analyzeAsmHeapAccess(MAsmJSHeapAccessType* ins);
+    template<typename MWasmMemoryAccessType>
+    void analyzeAsmHeapAccess(MWasmMemoryAccessType* ins);
 
   public:
     EffectiveAddressAnalysis(MIRGenerator* mir, MIRGraph& graph)
       : mir_(mir), graph_(graph)
     {}
 
     MOZ_MUST_USE bool analyze();
 };
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -12907,39 +12907,40 @@ class MAsmJSNeg
 
   public:
     INSTRUCTION_HEADER(AsmJSNeg)
     static MAsmJSNeg* NewAsmJS(TempAllocator& alloc, MDefinition* op, MIRType type) {
         return new(alloc) MAsmJSNeg(op, type);
     }
 };
 
-class MAsmJSHeapAccess
+class MWasmMemoryAccess
 {
     uint32_t offset_;
     uint32_t align_;
     Scalar::Type accessType_ : 8;
     bool needsBoundsCheck_;
     unsigned numSimdElems_;
     MemoryBarrierBits barrierBefore_;
     MemoryBarrierBits barrierAfter_;
 
   public:
-    explicit MAsmJSHeapAccess(Scalar::Type accessType, unsigned numSimdElems = 0,
-                              MemoryBarrierBits barrierBefore = MembarNobits,
-                              MemoryBarrierBits barrierAfter = MembarNobits)
+    explicit MWasmMemoryAccess(Scalar::Type accessType, uint32_t align, unsigned numSimdElems = 0,
+                               MemoryBarrierBits barrierBefore = MembarNobits,
+                               MemoryBarrierBits barrierAfter = MembarNobits)
       : offset_(0),
-        align_(Scalar::byteSize(accessType)),
+        align_(align),
         accessType_(accessType),
         needsBoundsCheck_(true),
         numSimdElems_(numSimdElems),
         barrierBefore_(barrierBefore),
         barrierAfter_(barrierAfter)
     {
         MOZ_ASSERT(numSimdElems <= ScalarTypeToLength(accessType));
+        MOZ_ASSERT(mozilla::IsPowerOfTwo(align));
     }
 
     uint32_t offset() const { return offset_; }
     uint32_t endOffset() const { return offset() + byteSize(); }
     uint32_t align() const { return align_; }
     Scalar::Type accessType() const { return accessType_; }
     unsigned byteSize() const {
         return Scalar::isSimdType(accessType())
@@ -12953,30 +12954,29 @@ class MAsmJSHeapAccess
     void setAlign(uint32_t a) { MOZ_ASSERT(mozilla::IsPowerOfTwo(a)); align_ = a; }
     MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
     MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
     bool isAtomicAccess() const { return (barrierBefore_|barrierAfter_) != MembarNobits; }
 };
 
 class MAsmJSLoadHeap
   : public MUnaryInstruction,
-    public MAsmJSHeapAccess,
+    public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
+    MAsmJSLoadHeap(MDefinition* base, const MWasmMemoryAccess& access)
       : MUnaryInstruction(base),
-        MAsmJSHeapAccess(access)
-    {
-        if (access.barrierBefore()|access.barrierAfter())
-            setGuard();         // Not removable
+        MWasmMemoryAccess(access)
+    {
+        if (access.barrierBefore() | access.barrierAfter())
+            setGuard(); // Not removable
         else
             setMovable();
 
-        MOZ_ASSERT(access.accessType() != Scalar::Uint8Clamped,
-                   "unexpected load heap in asm.js");
+        MOZ_ASSERT(access.accessType() != Scalar::Uint8Clamped, "unexpected load heap in asm.js");
         setResultType(ScalarTypeToMIRType(access.accessType()));
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap)
     TRIVIAL_NEW_WRAPPERS
 
     MDefinition* base() const { return getOperand(0); }
@@ -12990,26 +12990,25 @@ class MAsmJSLoadHeap
             return AliasSet::Store(AliasSet::AsmJSHeap);
         return AliasSet::Load(AliasSet::AsmJSHeap);
     }
     AliasType mightAlias(const MDefinition* def) const override;
 };
 
 class MAsmJSStoreHeap
   : public MBinaryInstruction,
-    public MAsmJSHeapAccess,
+    public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSStoreHeap(MDefinition* base, const MAsmJSHeapAccess& access,
-                    MDefinition* v)
+    MAsmJSStoreHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
       : MBinaryInstruction(base, v),
-        MAsmJSHeapAccess(access)
-    {
-        if (access.barrierBefore()|access.barrierAfter())
-            setGuard();         // Not removable
+        MWasmMemoryAccess(access)
+    {
+        if (access.barrierBefore() | access.barrierAfter())
+            setGuard(); // Not removable
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap)
     TRIVIAL_NEW_WRAPPERS
 
     MDefinition* base() const { return getOperand(0); }
     void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
@@ -13017,23 +13016,23 @@ class MAsmJSStoreHeap
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::AsmJSHeap);
     }
 };
 
 class MAsmJSCompareExchangeHeap
   : public MTernaryInstruction,
-    public MAsmJSHeapAccess,
+    public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSCompareExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
+    MAsmJSCompareExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
                               MDefinition* oldv, MDefinition* newv)
         : MTernaryInstruction(base, oldv, newv),
-          MAsmJSHeapAccess(access)
+          MWasmMemoryAccess(access)
     {
         setGuard();             // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSCompareExchangeHeap)
     TRIVIAL_NEW_WRAPPERS
@@ -13044,23 +13043,23 @@ class MAsmJSCompareExchangeHeap
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::AsmJSHeap);
     }
 };
 
 class MAsmJSAtomicExchangeHeap
   : public MBinaryInstruction,
-    public MAsmJSHeapAccess,
+    public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSAtomicExchangeHeap(MDefinition* base, const MAsmJSHeapAccess& access,
+    MAsmJSAtomicExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
                              MDefinition* value)
         : MBinaryInstruction(base, value),
-          MAsmJSHeapAccess(access)
+          MWasmMemoryAccess(access)
     {
         setGuard();             // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSAtomicExchangeHeap)
     TRIVIAL_NEW_WRAPPERS
@@ -13070,25 +13069,25 @@ class MAsmJSAtomicExchangeHeap
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::AsmJSHeap);
     }
 };
 
 class MAsmJSAtomicBinopHeap
   : public MBinaryInstruction,
-    public MAsmJSHeapAccess,
+    public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
     AtomicOp op_;
 
-    MAsmJSAtomicBinopHeap(AtomicOp op, MDefinition* base, const MAsmJSHeapAccess& access,
+    MAsmJSAtomicBinopHeap(AtomicOp op, MDefinition* base, const MWasmMemoryAccess& access,
                           MDefinition* v)
         : MBinaryInstruction(base, v),
-          MAsmJSHeapAccess(access),
+          MWasmMemoryAccess(access),
           op_(op)
     {
         setGuard();         // Not removable
         setResultType(MIRType::Int32);
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSAtomicBinopHeap)
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -217,18 +217,18 @@ class MIRGenerator
 
   public:
     AsmJSPerfSpewer& perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 
   public:
     const JitCompileOptions options;
 
-    bool needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const;
-    size_t foldableOffsetRange(const MAsmJSHeapAccess* access) const;
+    bool needsBoundsCheckBranch(const MWasmMemoryAccess* access) const;
+    size_t foldableOffsetRange(const MWasmMemoryAccess* access) const;
     size_t foldableOffsetRange(bool accessNeedsBoundsCheck, bool atomic) const;
 
   private:
     GraphSpewer gs_;
 
   public:
     GraphSpewer& graphSpewer() {
         return gs_;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -104,32 +104,32 @@ MIRGenerator::addAbortedPreliminaryGroup
             return;
     }
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!abortedPreliminaryGroups_.append(group))
         oomUnsafe.crash("addAbortedPreliminaryGroup");
 }
 
 bool
-MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
+MIRGenerator::needsBoundsCheckBranch(const MWasmMemoryAccess* access) const
 {
     // A heap access needs a bounds-check branch if we're not relying on signal
     // handlers to catch errors, and if it's not proven to be within bounds.
     // We use signal-handlers on x64, but on x86 there isn't enough address
     // space for a guard region.  Also, on x64 the atomic loads and stores
     // can't (yet) use the signal handlers.
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
     if (usesSignalHandlersForAsmJSOOB_ && !access->isAtomicAccess())
         return false;
 #endif
     return access->needsBoundsCheck();
 }
 
 size_t
-MIRGenerator::foldableOffsetRange(const MAsmJSHeapAccess* access) const
+MIRGenerator::foldableOffsetRange(const MWasmMemoryAccess* access) const
 {
     return foldableOffsetRange(access->needsBoundsCheck(), access->isAtomicAccess());
 }
 
 size_t
 MIRGenerator::foldableOffsetRange(bool accessNeedsBoundsCheck, bool atomic) const
 {
     // This determines whether it's ok to fold up to WasmImmediateRange
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2054,17 +2054,17 @@ Assembler::as_extdtr(LoadStore ls, int s
             extra_bits2 |= 0x2;
         }
         break;
       case 64:
         extra_bits2 = (ls == IsStore) ? 0x3 : 0x2;
         extra_bits1 = 0;
         break;
       default:
-        MOZ_CRASH("SAY WHAT?");
+        MOZ_CRASH("unexpected size in as_extdtr");
     }
     return writeInst(extra_bits2 << 5 | extra_bits1 << 20 | 0x90 |
                      addr.encode() | RT(rt) | mode | c);
 }
 
 BufferOffset
 Assembler::as_dtm(LoadStore ls, Register rn, uint32_t mask,
                 DTMMode mode, DTMWriteBack wb, Condition c)
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -151,17 +151,16 @@ struct ScratchDoubleScope : public AutoF
 // accessed with a single instruction.
 static const int32_t AsmJSGlobalRegBias = 1024;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2;
-static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (lr).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
@@ -277,17 +276,16 @@ struct ImmType : public ImmTag
 enum Index {
     Offset = 0 << 21 | 1<<24,
     PreIndex = 1 << 21 | 1 << 24,
     PostIndex = 0 << 21 | 0 << 24
     // The docs were rather unclear on this. It sounds like
     // 1 << 21 | 0 << 24 encodes dtrt.
 };
 
-// Seriously, wtf arm
 enum IsImmOp2_ {
     IsImmOp2    = 1 << 25,
     IsNotImmOp2 = 0 << 25
 };
 enum IsImmDTR_ {
     IsImmDTR    = 0 << 25,
     IsNotImmDTR = 1 << 25
 };
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1121,18 +1121,18 @@ void
 MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 8, mode, rt, addr, cc);
 }
 
 // Specialty for moving N bits of data, where n == 8,16,32,64.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                          Register rn, Register rm, Register rt,
-                          Index mode, Assembler::Condition cc, unsigned shiftAmount)
+                                    Register rn, Register rm, Register rt,
+                                    Index mode, Assembler::Condition cc, unsigned shiftAmount)
 {
     if (size == 32 || (size == 8 && !IsSigned))
         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc);
 
     ScratchRegisterScope scratch(asMasm());
 
     if (shiftAmount != 0) {
         MOZ_ASSERT(rn != scratch);
@@ -1777,17 +1777,18 @@ MacroAssemblerARM::ma_vldr(VFPAddr addr,
 
 BufferOffset
 MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc)
 {
     return ma_vdtr(IsLoad, addr, dest, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc)
+MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift,
+                           Condition cc)
 {
     ScratchRegisterScope scratch(asMasm());
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
     return ma_vldr(Address(scratch, 0), src, cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc)
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -297,24 +297,26 @@ class MacroAssemblerARM : public Assembl
     void ma_ldrb(DTRAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrh(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrsh(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrsb(EDtrAddr addr, Register rt, Index mode = Offset, Condition cc = Always);
     void ma_ldrd(EDtrAddr addr, Register rt, DebugOnly<Register> rt2, Index mode = Offset, Condition cc = Always);
     void ma_strb(Register rt, DTRAddr addr, Index mode = Offset, Condition cc = Always);
     void ma_strh(Register rt, EDtrAddr addr, Index mode = Offset, Condition cc = Always);
     void ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode = Offset, Condition cc = Always);
+
     // Specialty for moving N bits of data, where n == 8,16,32,64.
     BufferOffset ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                          Register rn, Register rm, Register rt,
-                          Index mode = Offset, Condition cc = Always, unsigned scale = TimesOne);
+                                  Register rn, Register rm, Register rt,
+                                  Index mode = Offset, Condition cc = Always, unsigned scale = TimesOne);
 
     BufferOffset ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                          Register rn, Imm32 offset, Register rt,
-                          Index mode = Offset, Condition cc = Always);
+                                  Register rn, Imm32 offset, Register rt,
+                                  Index mode = Offset, Condition cc = Always);
+
     void ma_pop(Register r);
     void ma_push(Register r);
 
     void ma_vpop(VFPRegister r);
     void ma_vpush(VFPRegister r);
 
     // Barriers.
     void ma_dmb(BarrierOption option=BarrierSY);
@@ -388,24 +390,23 @@ class MacroAssemblerARM : public Assembl
 
     // Transfer (do not coerce) a gpr into a float
     void ma_vxfer(Register src, FloatRegister dest, Condition cc = Always);
     // Transfer (do not coerce) a couple of gpr into a double
     void ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc = Always);
 
     BufferOffset ma_vdtr(LoadStore ls, const Address& addr, VFPRegister dest, Condition cc = Always);
 
-
     BufferOffset ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc = Always);
     BufferOffset ma_vldr(const Address& addr, VFPRegister dest, Condition cc = Always);
-    BufferOffset ma_vldr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always);
+    BufferOffset ma_vldr(VFPRegister src, Register base, Register index,
+                         int32_t shift = defaultShift, Condition cc = Always);
 
     BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always);
     BufferOffset ma_vstr(VFPRegister src, const Address& addr, Condition cc = Always);
-
     BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
                          int32_t offset, Condition cc = Always);
 
     void ma_call(ImmPtr dest);
 
     // Float registers can only be loaded/stored in continuous runs when using
     // vstm/vldm. This function breaks set into continuous runs and loads/stores
     // them at [rm]. rm will be modified and left in a state logically suitable
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -119,17 +119,16 @@ REGISTER_CODE_LIST(IMPORT_VIXL_VREGISTER
 
 static constexpr ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static constexpr Register AsmJSIonExitRegCallee = r8;
 static constexpr Register AsmJSIonExitRegE0 = r0;
 static constexpr Register AsmJSIonExitRegE1 = r1;
 static constexpr Register AsmJSIonExitRegE2 = r2;
-static constexpr Register AsmJSIonExitRegE3 = r3;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register.
 static constexpr Register AsmJSIonExitRegReturnData = r2;
 static constexpr Register AsmJSIonExitRegReturnType = r3;
 static constexpr Register AsmJSIonExitRegD0 = r0;
 static constexpr Register AsmJSIonExitRegD1 = r1;
 static constexpr Register AsmJSIonExitRegD2 = r4;
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -112,17 +112,16 @@ static MOZ_CONSTEXPR_VAR FloatRegister S
 // accessed with a single instruction.
 static const int32_t AsmJSGlobalRegBias = 32768;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = t0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = a0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = a1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = a2;
-static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = a3;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (t8).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -42,17 +42,16 @@ static MOZ_CONSTEXPR_VAR Register IntArg
 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register GlobalReg = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register HeapReg = { Registers::invalid_reg };
 
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = { Registers::invalid_reg };
-static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = { Registers::invalid_reg };
 
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = { Registers::invalid_reg };
 
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = { Registers::invalid_reg };
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -149,17 +149,16 @@ static MOZ_CONSTEXPR_VAR uint32_t NumFlo
 static MOZ_CONSTEXPR_VAR FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
 #endif
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r10;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = rax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = rdi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = rbx;
-static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = rsi;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -920,17 +920,17 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
     }
 
     memoryBarrier(mir->barrierAfter());
 
     masm.append(AsmJSMemoryAccess(before, wasm::MemoryAccess::CarryOn));
 }
 
 static void
-MaybeAddAtomicsBoundsCheck(MacroAssemblerX64& masm, MAsmJSHeapAccess* mir, Register ptr)
+MaybeAddAtomicsBoundsCheck(MacroAssemblerX64& masm, MWasmMemoryAccess* mir, Register ptr)
 {
     if (!mir->needsBoundsCheck())
         return;
 
     // Note that we can't use the same machinery as normal asm.js loads/stores
     // since signal-handler bounds checking is not yet implemented for atomic
     // accesses.
     uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(-mir->endOffset())).offset();
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -158,32 +158,32 @@ LIRGeneratorX64::visitAsmJSUnsignedToFlo
 void
 LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
-    LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
+    LAllocation baseAlloc = gen->needsBoundsCheckBranch(ins)
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
 
     define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
 }
 
 void
 LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
-    LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
+    LAllocation baseAlloc = gen->needsBoundsCheckBranch(ins)
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
 
     LAsmJSStoreHeap* lir = nullptr;  // initialize to silence GCC warning
     switch (ins->accessType()) {
       case Scalar::Int8:
       case Scalar::Uint8:
       case Scalar::Int16:
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -423,23 +423,23 @@ CodeGeneratorX86Shared::visitOffsetBound
     // after the access to restore asm.js invariants.
     masm.movslq(oolCheck->ptrReg(), oolCheck->ptrReg());
 #endif
 
     masm.jmp(oolCheck->rejoin());
 }
 
 void
-CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access,
+CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* access,
                                                    const MInstruction* mir,
                                                    Register ptr, Label* maybeFail)
 {
     // Emit a bounds-checking branch for |access|.
 
-    MOZ_ASSERT(gen->needsAsmJSBoundsCheckBranch(access));
+    MOZ_ASSERT(gen->needsBoundsCheckBranch(access));
 
     Label* pass = nullptr;
 
     // If we have a non-zero offset, it's possible that |ptr| itself is out of
     // bounds, while adding the offset computes an in-bounds address. To catch
     // this case, we need a second branch, which we emit out of line since it's
     // unlikely to be needed in normal programs.
     if (access->offset() != 0) {
@@ -462,35 +462,35 @@ CodeGeneratorX86Shared::emitAsmJSBoundsC
 
     if (pass)
         masm.bind(pass);
 
     masm.append(wasm::BoundsCheck(cmpOffset));
 }
 
 bool
-CodeGeneratorX86Shared::maybeEmitThrowingAsmJSBoundsCheck(const MAsmJSHeapAccess* access,
+CodeGeneratorX86Shared::maybeEmitThrowingAsmJSBoundsCheck(const MWasmMemoryAccess* access,
                                                           const MInstruction* mir,
                                                           const LAllocation* ptr)
 {
-    if (!gen->needsAsmJSBoundsCheckBranch(access))
+    if (!gen->needsBoundsCheckBranch(access))
         return false;
 
     emitAsmJSBoundsCheckBranch(access, mir, ToRegister(ptr), nullptr);
     return true;
 }
 
 bool
 CodeGeneratorX86Shared::maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
                                                       OutOfLineLoadTypedArrayOutOfBounds** ool)
 {
     MOZ_ASSERT(!Scalar::isSimdType(mir->accessType()));
     *ool = nullptr;
 
-    if (!gen->needsAsmJSBoundsCheckBranch(mir))
+    if (!gen->needsBoundsCheckBranch(mir))
         return false;
 
     Label* rejoin = nullptr;
     if (!mir->isAtomicAccess()) {
         *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(ins->output()),
                                                                mir->accessType());
         addOutOfLineCode(*ool, mir);
         rejoin = (*ool)->entry();
@@ -502,33 +502,33 @@ CodeGeneratorX86Shared::maybeEmitAsmJSLo
 
 bool
 CodeGeneratorX86Shared::maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins,
                                                        Label** rejoin)
 {
     MOZ_ASSERT(!Scalar::isSimdType(mir->accessType()));
 
     *rejoin = nullptr;
-    if (!gen->needsAsmJSBoundsCheckBranch(mir))
+    if (!gen->needsBoundsCheckBranch(mir))
         return false;
 
     if (!mir->isAtomicAccess())
         *rejoin = alloc().lifoAlloc()->newInfallible<Label>();
 
     emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ins->ptr()), *rejoin);
     return true;
 }
 
 void
-CodeGeneratorX86Shared::cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access,
+CodeGeneratorX86Shared::cleanupAfterAsmJSBoundsCheckBranch(const MWasmMemoryAccess* access,
                                                            Register ptr)
 {
     // Clean up after performing a heap access checked by a branch.
 
-    MOZ_ASSERT(gen->needsAsmJSBoundsCheckBranch(access));
+    MOZ_ASSERT(gen->needsBoundsCheckBranch(access));
 
 #ifdef JS_CODEGEN_X64
     // If the offset is 0, we don't use an OffsetBoundsCheck.
     if (access->offset() != 0) {
         // Zero out the high 32 bits, in case the OffsetBoundsCheck code had to
         // sign-extend (movslq) the pointer value to get wraparound to work.
         masm.movl(ptr, ptr);
     }
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -90,35 +90,35 @@ class CodeGeneratorX86Shared : public Co
 
         void accept(CodeGeneratorX86Shared* codegen) {
             codegen->visitOutOfLineSimdFloatToIntCheck(this);
         }
     };
 
   private:
     void
-    emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins,
+    emitAsmJSBoundsCheckBranch(const MWasmMemoryAccess* mir, const MInstruction* ins,
                                Register ptr, Label* fail);
 
   public:
     // For SIMD and atomic loads and stores (which throw on out-of-bounds):
     bool
-    maybeEmitThrowingAsmJSBoundsCheck(const MAsmJSHeapAccess* mir, const MInstruction* ins,
+    maybeEmitThrowingAsmJSBoundsCheck(const MWasmMemoryAccess* mir, const MInstruction* ins,
                                       const LAllocation* ptr);
 
     // For asm.js plain and atomic loads that possibly require a bounds check:
     bool
     maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
                                   OutOfLineLoadTypedArrayOutOfBounds** ool);
 
     // For asm.js plain and atomic stores that possibly require a bounds check:
     bool
     maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins, Label** rejoin);
 
-    void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr);
+    void cleanupAfterAsmJSBoundsCheckBranch(const MWasmMemoryAccess* mir, Register ptr);
 
     NonAssertingLabel deoptLabel_;
 
     Operand ToOperand(const LAllocation& a);
     Operand ToOperand(const LAllocation* a);
     Operand ToOperand(const LDefinition* def);
 
     MoveOperand toMoveOperand(LAllocation a) const;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -97,17 +97,16 @@ static MOZ_CONSTEXPR_VAR Register WasmTa
 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = edx;
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = edx;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = ebx;
-static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = edx;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = edx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -206,52 +206,52 @@ LIRGeneratorX86::visitAsmJSUnsignedToFlo
 void
 LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
-    LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
+    LAllocation baseAlloc = gen->needsBoundsCheckBranch(ins)
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
 
     define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
 }
 
 void
 LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     // For simplicity, require a register if we're going to emit a bounds-check
     // branch, so that we don't have special cases for constants.
-    LAllocation baseAlloc = gen->needsAsmJSBoundsCheckBranch(ins)
+    LAllocation baseAlloc = gen->needsBoundsCheckBranch(ins)
                             ? useRegisterAtStart(base)
                             : useRegisterOrZeroAtStart(base);
 
     LAsmJSStoreHeap* lir = nullptr;
     switch (ins->accessType()) {
       case Scalar::Int8: case Scalar::Uint8:
         // See comment for LIRGeneratorX86::useByteOpRegister.
         lir = new(alloc()) LAsmJSStoreHeap(baseAlloc, useFixed(ins->value(), eax));
         break;
       case Scalar::Int16: case Scalar::Uint16:
       case Scalar::Int32: case Scalar::Uint32:
       case Scalar::Float32: case Scalar::Float64:
       case Scalar::Float32x4:
       case Scalar::Int8x16:
       case Scalar::Int16x8:
       case Scalar::Int32x4:
-          // For now, don't allow constant values. The immediate operand
-          // affects instruction layout which affects patching.
-          lir = new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()));
-          break;
+        // For now, don't allow constant values. The immediate operand affects
+        // instruction layout which affects patching.
+        lir = new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()));
+        break;
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("unexpected array type");
     }
     add(lir, ins);
 }
 
 void
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -340,17 +340,18 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,       1
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1, JSEXN_TYPEERR, "asm.js link error: {0}")
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR,     "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR,     "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,         "wasm indirect call signature mismatch")
-MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be a typed array")
+MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
+MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,         "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_ERR,         "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_ERR,         "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_ERR,         "integer divide by zero")
 MSG_DEF(JSMSG_WASM_OVERRECURSED,       0, JSEXN_INTERNALERR, "call stack exhausted")
 
 // Proxy
--- a/js/src/jsapi-tests/testPromise.cpp
+++ b/js/src/jsapi-tests/testPromise.cpp
@@ -1,17 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "jsapi.h"
+
 #include "jsapi-tests/tests.h"
 
+using namespace JS;
+
 static bool executor_called = false;
 
 static bool
 PromiseExecutor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].toObject().is<JSFunction>());
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4637,16 +4637,21 @@ JS_GetInterruptCallback(JSRuntime* rt)
     return rt->interruptCallback;
 }
 
 /************************************************************************/
 
 /*
  * Promises.
  */
+JS_PUBLIC_API(void)
+JS::SetGetIncumbentGlobalCallback(JSRuntime* rt, JSGetIncumbentGlobalCallback callback)
+{
+    rt->getIncumbentGlobalCallback = callback;
+}
 
 JS_PUBLIC_API(void)
 JS::SetEnqueuePromiseJobCallback(JSRuntime* rt, JSEnqueuePromiseJobCallback callback,
                                  void* data /* = nullptr */)
 {
     rt->enqueuePromiseJobCallback = callback;
     rt->enqueuePromiseJobCallbackData = data;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -577,19 +577,23 @@ typedef void
 (* JSWeakPointerZoneGroupCallback)(JSRuntime* rt, void* data);
 
 typedef void
 (* JSWeakPointerCompartmentCallback)(JSRuntime* rt, JSCompartment* comp, void* data);
 
 typedef bool
 (* JSInterruptCallback)(JSContext* cx);
 
+typedef JSObject*
+(* JSGet